前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >基于注解的用户权限拦截Spring HandlerInterceptor

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

作者头像
喜欢天文的pony站长
发布2020-06-29 11:54:03
1K0
发布2020-06-29 11:54:03
举报
文章被收录于专栏:RabbitMQ实战RabbitMQ实战

Spring Boot (v2.0.5.RELEASE)

  • 程序中有些资源(接口)是需要用户登录才能够使用的,或者是具有某种角色的用户(比如普通登录用户,或者系统管理员等)才能使用,本篇文章先为大家讲解如何控制使用某接口要求用户必须登录。
  • 实现的思路是
    1. 首先定义注解 @LoginUser,该注解用于标注哪些接口需要进行拦截
    2. 定义拦截器,拦截标注了 @LoginUser注解的接口
    3. 拦截之后判断该用户目前是不是处于登陆状态,如果是登陆状态则放行该请求,如果未登录则提示登陆
    4. 给方法或者类打上 @LoginUser注解进行测试

1. 定义标注注解 @LoginUser

代码语言:javascript
复制
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)
 
@Documented
 
public @interface LoginUser {
 
 /**
 
     * 要求的用户角色
 
     *
 
     * @return
 
     */
 
 Role role() default Role.Normal;
 
}
 

2. 定义拦截器 LoginUserInterceptor

代码语言:javascript
复制
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的方法进行拦截
 
 */
 
@Component
 
public 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();
 
 }
 
}
 

3. 注册拦截器

代码语言:javascript
复制
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.
 
 */
 
@SpringBootConfiguration
 
public 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("/**");
 
 }
 
}
 

4. 测试(可分别将注解打在类上和方法上进行测试)

代码语言:javascript
复制
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)
 
@RestController
 
public 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();
 
 }
 
}
 

4.1 测试未登录情况下调用标记了 @LoginUser的获取当前登陆用户信息接口

4.2 登录

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

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

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

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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-09-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 喜欢天文 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档