• springboot 点滴(3)springboot ThreadLocal实现单机权限认证


    Springboot中权限认证的基本方案:
    可以对URL进行HandlerInterceptor拦截,然后权限验证。

    Client登录成功后,每次发送请求时,会将token等信息存放到header中。
    Server收到请求,在HandlerInterceptor中从header获取用户的信息(userId,token等),然后存入上下文context;在后面Controller中就可以从上下文context中获取用户的信息做权限认证。

    • 单机应用上下文context:
      Threadlocal,在进程内部,我们可以使用ThreadLocal传递应用上下文的方式.。

    • 分布式系统上下文context:
      需要借助以下方式实现:
      1)数据库; 2)Redis ;3)文件系统。

    1 HandlerInterceptor 拦截ThreadLocal实现上下文

    这里讲一下单机应用Threadlocal。
    ThreadLocal不是线程,是线程变量;.ThreadLocal 是 Java中一种较为特殊的线程绑定机制.通过ThreadLocal存取的数据,总是与当前线程相关。

    第1步:创建User对象:

    public class User {
        public String userId;
        public String orgId;
        public String token;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    第2步:创建ThreadLocal对象来存取User:

    public class ContextManager {
        private static final ThreadLocal<User>  threadLocal=  new ThreadLocal();
    
        public static User getUser() {
            return threadLocal.get();
        }
    
        public static void addUser(User user){
            threadLocal.set(user);
        }
    
        public static void removeUser(){
            threadLocal.remove();
        }
    
        public static String  getUserId(){
            return threadLocal.get().userId;
        }
    
        public static String  getToken(){
            return threadLocal.get().token;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    第3步:创建HandlerInterceptor:

    public class AuthInterceptor implements HandlerInterceptor {
        @Autowired
        private HttpRequest httpRequest;
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("threadId:"+Thread.currentThread().getId());
            String userId = (String)request.getHeader("userId");
            String token = (String)request.getHeader("token");
            if(!StringUtils.hasLength(userId)){
                response.getWriter().println("please login");
                return false;
            }
            if(!StringUtils.hasLength(token)){
                response.getWriter().println("please login");
                return false;
            }
            //将用户相关的信息存入threadlocal中
            User user = new User();
            user.userId = userId;
            user.token = token;
            ContextManager.addUser(user);
            return true;
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("afterCompletion:"+Thread.currentThread().getId());
            ContextManager.removeUser();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    第4步:创建WebMvcConfigurer:
    Spring Boot 1.5版本:重写WebMvcConfigurerAdapter的方法来添加自定义拦截器。
    SpringBoot 2.0 版本后,该类被标记为@Deprecated(弃用)。官方推荐直接实现WebMvcConfigurer或者直接继承WebMvcConfigurationSupport,
    1)实现WebMvcConfigurer接口(推荐),
    2)继承WebMvcConfigurationSupport类

    @Configuration
    public class LWebMvcConfigurer implements WebMvcConfigurer {
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            ArrayList<String> whiteList = new ArrayList<>();
            whiteList.add("/login");
            whiteList.add("/logout");
            registry.addInterceptor(new AuthInterceptor())
                    .excludePathPatterns(whiteList)
                    .addPathPatterns("/**");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    第5步:创建HelloController 来验证从threadlocal中获取数据:

    @RestController
    public class HelloController {
    
        @RequestMapping(value = "/hello", method = RequestMethod.GET)
        public String hello() {
            String token = ContextManager.getToken();
            if(!StringUtils.hasLength(token)){
                return "please login!";
            }
            log.info("token:"+ token);
            String userId = ContextManager.getUserId();
            if(!StringUtils.hasLength(userId)){
                return "please login!";
            }
            log.info("userId:"+ userId);
            return  "hello world!";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    2 测试验证

    2.1 请求有token

    在这里插入图片描述
    controller中获取token:在这里插入图片描述

    2.2 请求没有token

    在这里插入图片描述
    需要用户的请求header中增加token。

  • 相关阅读:
    如何用Python让你的电脑说话
    linux 应急响应工具整理列表
    云计算学习笔记——第五章 网络虚拟化
    python将字符串转换大小写的四大函数——lower、upper、capitalize、title函数
    【827. 最大人工岛】
    c++基本图形绘制
    《算法竞赛进阶指南》蚯蚓
    程序员Debug方式大全,80%的程序员到不了第5级!
    25、MySQL 导出数据
    JVM内存模型和结构详解(五大模型图解)
  • 原文地址:https://blog.csdn.net/afei8080/article/details/128045015