继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

基于注解的用户权限拦截Spring HandlerInterceptor

慕神8447489
关注TA
已关注
手记 1134
粉丝 172
获赞 955

Spring Boot (v2.0.5.RELEASE)

  • 程序中有些资源(接口)是需要用户登录才能够使用的,或者是具有某种角色的用户(比如普通登录用户,或者系统管理员等)才能使用,本篇文章先为大家讲解如何控制使用某接口要求用户必须登录。

  • 实现的思路是

  1. 首先定义注解@LoginUser,该注解用于标注哪些接口需要进行拦截

  2. 定义拦截器,拦截标注了@LoginUser注解的接口

  3. 拦截之后判断该用户目前是不是处于登陆状态,如果是登陆状态则放行该请求,如果未登录则提示登陆

  4. 给方法或者类打上@LoginUser注解进行测试

  1. 定义标注注解@LoginUser

package com.futao.springmvcdemo.annotation;import com.futao.springmvcdemo.model.enums.Role;import java.lang.annotation.*;/**
 * @author futao
 * Created on 2018/9/19-14:39.
 * 登陆用户,用户角色
 */@Target(value = {
        ElementType.METHOD,
        ElementType.TYPE
})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface LoginUser {    /**
     * 要求的用户角色
     *
     * @return
     */
    Role role() default Role.Normal;
}

2。 定义拦截器LoginUserInterceptor

package com.futao.springmvcdemo.annotation.impl;import com.alibaba.fastjson.JSON;import com.futao.springmvcdemo.annotation.LoginUser;import com.futao.springmvcdemo.model.entity.constvar.ErrorMessage;import com.futao.springmvcdemo.model.system.RestResult;import com.futao.springmvcdemo.model.system.SystemConfig;import com.futao.springmvcdemo.utils.ThreadLocalUtils;import org.apache.commons.lang3.ObjectUtils;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.stereotype.Component;import org.springframework.web.method.HandlerMethod;import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;import javax.annotation.Resource;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;/**
 * @author futao
 * Created on 2018/9/19-14:44.
 * 对请求标记了LoginUser的方法进行拦截
 */@Componentpublic class LoginUserInterceptor extends HandlerInterceptorAdapter {    private static final Logger logger = LoggerFactory.getLogger(LoginUserInterceptor.class);    @Resource
    private ThreadLocalUtils<String> threadLocalUtils;    /**
     * 在请求到达Controller之前进行拦截并处理
     *
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {        if (handler instanceof HandlerMethod) {            //注解在方法上
            LoginUser loginUserAnnotation = ((HandlerMethod) handler).getMethodAnnotation(LoginUser.class);            //注解在类上
            LoginUser classLoginUserAnnotation = ((HandlerMethod) handler).getMethod().getDeclaringClass().getAnnotation(LoginUser.class);            if (ObjectUtils.anyNotNull(loginUserAnnotation, classLoginUserAnnotation)) {
                HttpSession session = request.getSession(false);                //session不为空
                if (ObjectUtils.allNotNull(session)) {
                    String loginUser = (String) session.getAttribute(SystemConfig.LOGIN_USER_SESSION_KEY);                    if (ObjectUtils.allNotNull(loginUser)) {
                        System.out.println("当前登陆用户为:" + loginUser);                        //将当前用户的信息存入threadLocal中
                        threadLocalUtils.set(loginUser);
                    } else {
                        System.out.println("用户不存在");                        return false;
                    }
                } else {//session为空,用户未登录
                    RestResult restResult = new RestResult(false, "-1", ErrorMessage.NOT_LOGIN, ErrorMessage.NOT_LOGIN.substring(6));
                    response.getWriter().append(JSON.toJSONString(restResult));                    return false;
                }
            }
        }        return true;
    }    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {        //释放threadLocal资源
        threadLocalUtils.remove();
    }
}
  1. 注册拦截器

package com.futao.springmvcdemo.annotation;import com.futao.springmvcdemo.annotation.impl.LoginUserInterceptor;import com.futao.springmvcdemo.annotation.impl.RequestLogInterceptor;import com.futao.springmvcdemo.annotation.impl.SignInterceptor;import org.springframework.boot.SpringBootConfiguration;import org.springframework.web.servlet.config.annotation.InterceptorRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;import javax.annotation.Resource;/**
 * @author futao
 * Created on 2018/9/18-15:15.
 */@SpringBootConfigurationpublic class WebMvcConfiguration implements WebMvcConfigurer {    @Resource
    private SignInterceptor signInterceptor;    @Resource
    private LoginUserInterceptor loginUserInterceptor;    @Resource
    private RequestLogInterceptor requestLogInterceptor;    /**
     * addInterceptor()的顺序需要严格按照程序的执行的顺序
     *
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(requestLogInterceptor).addPathPatterns("/**");
        registry.addInterceptor(loginUserInterceptor).addPathPatterns("/**");        //  "/**"和"/*"是有区别的
        registry.addInterceptor(signInterceptor).addPathPatterns("/**");
    }
}
  1. 测试(可分别将注解打在类上和方法上进行测试)

package com.futao.springmvcdemo.controller;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.JSONArray;import com.alibaba.fastjson.JSONObject;import com.futao.springmvcdemo.annotation.LoginUser;import com.futao.springmvcdemo.model.entity.User;import com.futao.springmvcdemo.model.system.SystemConfig;import com.futao.springmvcdemo.service.UserService;import org.apache.commons.lang3.ObjectUtils;import org.springframework.http.MediaType;import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpSession;import java.util.List;import java.util.UUID;/**
 * @author futao
 * Created on 2018/9/19-15:05.
 */@RequestMapping(path = "User", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)@RestControllerpublic class UserController {    @Resource
    private UserService userService;    /**
     * 获取当前的登陆的用户信息,其实是从threadLocal中获取
     *
     * @return
     */
    @LoginUser
    @GetMapping(path = "my")    public JSONObject my() {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("当前的登陆的用户是:", userService.currentUser());        return jsonObject;
    }    /**
     * 模拟登陆接口
     *
     * @param mobile
     * @param request
     * @return
     */
    @PostMapping(path = "login")    public JSONObject login(
            @RequestParam("mobile") String mobile,
            HttpServletRequest request
    ) {
        HttpSession session = request.getSession();
        session.setAttribute(SystemConfig.LOGIN_USER_SESSION_KEY, String.valueOf(UUID.randomUUID()));
        session.setMaxInactiveInterval(SystemConfig.SESSION_INVALIDATE_SECOND);        return new JSONObject();
    }
}
  1. 测试
    4.1 未登录情况下调用标记了@LoginUser的获取当前登陆用户信息接口

    webp

    未登录


    4.2 登录

    webp

    登录操作


    4.3 登录之后调用调用标记了@LoginUser的获取当前登陆用户信息接口

    webp

    登陆之后

稍微解释一下上面登陆和获取用户信息的逻辑:
用户请求登陆之后,会为该用户在系统中生成一个HttpSession,同时在系统中有一个Map来存放所有的session信息,该Mapkey为一个随机字符串,valuesession对象在系统中的堆地址,在登陆请求完成之后,系统会将该sesionkey值以cookie(JSESSIONID)的形式写回浏览器。

webp

设置cookie


用户下次登陆的时候,请求中会自动带上该cookie,所以我们在标记了需要登陆的@LoginUser注解的请求到达处理逻辑之前进行拦截,就是从cookie中(JSESSIONID)取出sessionkey值,如果没有该cookie,则代表用户没有登陆,如果有该cookie,再在存放cookiemap中取,如果没有取到,则代表用户的session已经过期了,需要重新登陆,或者cookie是伪造的。
拿到了登陆用户的session之后,我们去Map中获取对应的值,一般是用户的id,在通过这个用户id,可以去数据库查该用户的信息,查到用户的信息之后将用户信息放入threadLocal中,然后就可以在任何地方get()到当前登陆的用户信息了,非常方便。


使用上面的基于注解的拦截器可以实现很多功能,比如动态的第三方接口验签,和系统日志记录(不需要注解)等

webp

日志系统



作者:FutaoSmile丶
链接:https://www.jianshu.com/p/657fa7118e84


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP