• token验证的方法


    统一token处理

    排除token校验注解类
    为不需要校验 token 的方法定义注解
    @Documented //标记注解
    @Target(ElementType.METHOD) //指定作用在方法上 对方法拦截
    @Retention(RetentionPolicy.RUNTIME) //作用域 在运行时有效

    NoAuthorization.java

    1. import java.lang.annotation.*;
    2. /**
    3. * 包含该注解的方法 不需要校验token 直接放行
    4. * @author 陌路
    5. * @date 2022-03-19
    6. * @apiNote token统一处理
    7. */
    8. @Documented//标记注解
    9. @Target(ElementType.METHOD) //指定作用在方法上 对方法拦截
    10. @Retention(RetentionPolicy.RUNTIME) //作用域 在运行时有效
    11. public @interface NoAuthorization {
    12. /** ---------@定义注解---------
    13. * 元注解
    14. * @Target({METHOD,TYPE}) 表示这个注解可以用用在类/接口上,还可以用在方法上
    15. * @Retention(RetentionPolicy.RUNTIME) 表示这是一个运行时注解,即运行起来之后,
    16. * 才获取注解中的相关信息,而不像基本注解如@Override 那种。
    17. * @Inherited 表示这个注解可以被子类继承
    18. * @Documented 表示当执行javadoc的时候,本注解会生成相关文档(标记)
    19. *
    20. * 自定义注解的使用:
    21. * 该注解定义好之后,将该注解加在方法上@NoAuthorization即可,
    22. * 若注解中存在属性需要赋值,则直接可以在注解中赋值即可,
    23. * eg:public @interface ZjObj{
    24. * String name();
    25. * Integer sex();
    26. * }
    27. * eg: @ZjObj(属性="值") -> @ZjObj(name="张三",sex=1)
    28. *
    29. * 也可这样定义注解,效果都是一样的
    30. * @Target(value = { ElementType.ANNOTATION_TYPE })
    31. * @Retention(RetentionPolicy.RUNTIME)
    32. * @Inherited
    33. * @Documented
    34. *
    35. * ---------@解析注解@使用注解---------
    36. * * 若注解中有属性,则可以直接通过反射来获取属性值
    37. * * 相关配置信息本来是以属性的方式存放的,现在改为了以注解的方式
    38. * *
    39. * * @解析注解: 通过反射,获取这个类上的注解对象
    40. * * ZjObj zjObj = class.foraName(ZjObj.class);
    41. * * 拿到注解对象之后,通过方法,获取各个注解元素的值:
    42. * * String name = zjObj.name();
    43. * * Integer sex = zjObj.sex ();
    44. */
    45. }

    本地线程缓存类
    封装获取当前登录人数据信息类

    在 TokenInterceptor 将获取到的user数据信息添加到 本地线程UserThreadLocal中去
    TokenInterceptor类中实现了HandlerInterceptor拦截器,对请求进行了拦截
    并对token进行了校验,token有效就会把user数据信息存储到UserThreadLocal当前类中
    通过调用该类的get()方法时,即可获取到user的数据信息

    UserThreadLocal.java

    1. /**
    2. * 获取当前登录人信息
    3. * @author 陌路
    4. * @date 2022-03-19
    5. * @apiNote 封装当前登录用户信息
    6. */
    7. public class UserThreadLocal {
    8. private static final ThreadLocal<User> LOCAL = new ThreadLocal<User>();
    9. /**
    10. * `TokenInterceptor`类中实现了`HandlerInterceptor`拦截器,对请求进行了拦截
    11. * 并对token进行了校验,token有效就会把`user`数据信息存储到当前类中
    12. * 通过调用该类的`get()`方法时,即可获取到`user`的数据信息
    13. */
    14. //构造方法私有化,不运行外部实例化该对象
    15. private UserThreadLocal() {
    16. }
    17. public static void set(User user) {
    18. LOCAL.set(user);
    19. }
    20. public static User get() {
    21. return LOCAL.get();
    22. }
    23. }

    token校验类
    定义方法的请求拦截器,判断用户请求的方法是否需要校验token信息,preHandle 前置拦截
    此拦截器是在执行用户请求的方法前拦截到的,该拦截器结束之前,不会执行用户请求的方法
    当该拦截器执行完成后,根据拦截器返回结果(true|false)判断是否继续执行用户的请求
    preHandle 返回 false 时,用户请求将不再继续执行,为 true 时校验通过才会继续执行

    思路:
    首先判断用户请求的方法中是否包含了@NoAuthorization注解 若包含了该注解 则放行
    如果不包含该注解,说明需要对token进行校验,继续执行下面的校验代码
    校验:首先判断请求头中是否包含 "Authorization"请求头数据 不包含则结束校验 不予访问
    若包含,则需要解析token数据拿到user对象,不为空就把当前user对象存储到UserThreadLocal线程中去并返回true验证成功

    TokenInterceptor.java

    1. import org.apache.commons.lang3.StringUtils;
    2. import org.springframework.beans.factory.annotation.Autowired;
    3. import org.springframework.stereotype.Component;
    4. import org.springframework.web.method.HandlerMethod;
    5. import org.springframework.web.servlet.HandlerInterceptor;
    6. import javax.servlet.http.HttpServletRequest;
    7. import javax.servlet.http.HttpServletResponse;
    8. /**
    9. * 请求拦截器 统一token校验
    10. * @author 陌路
    11. * @date 2022-03-19
    12. * @apiNote 统一token校验
    13. */
    14. @Component
    15. public class TokenInterceptor implements HandlerInterceptor {
    16. @Autowired
    17. private UserService userService;
    18. @Override
    19. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
    20. throws Exception {
    21. /** 思路
    22. * 首先判断请求的方法中是否包含了@NoAuthorization注解 若包含了此注解 则不作处理
    23. * 包含该@NoAuthorization注解 表示不需要做token的验证 直接return true 放行即可
    24. * 如果不包含该注解,说明需要对token进行校验,则继续执行下面的代码
    25. * 校验:首先判断请求头中是否包含 "Authorization" 请求头数据 不包含则结束校验 不予访问
    26. * 若包含 则需要解析token数据 拿到 user 对象,判断user对象是否为null
    27. *null 说明不存在,直接返回false,不予访问,重新登录
    28. * 不为空 则需要把当前user对象存储到 UserThreadLocal 线程中去 并返回true 验证成功
    29. */
    30. // 如果 handler 是 HandlerMethod 类型,则把 handler(Object类型)转为HandlerMethod类型
    31. if (handler instanceof HandlerMethod) {
    32. HandlerMethod handlerMethod = (HandlerMethod) handler;
    33. // 获取将要访问的方法名,根据方法名获取方法上的 NoAuthorization 注解,判断该注解是否存在
    34. NoAuthorization noAuthorization = handlerMethod
    35. .getMethod()
    36. .getAnnotation(NoAuthorization.class);
    37. //若方法中存在该 NoAuthorization 注解,则表示无需校验 返回true
    38. if (noAuthorization != null) {
    39. return true;
    40. }
    41. }
    42. //获取到请求头中的头信息(token)
    43. String token = request.getHeader("Authorization");
    44. //判断token是否存在
    45. if (StringUtils.isNotEmpty(token)) {
    46. //存在则根据token解析user数据
    47. User user = this.userService.queryUserByToken(token);
    48. //判断解析的user数据是否为null
    49. if (null != user) {
    50. //不为空 则将user信息存储到线程中
    51. UserThreadLocal.set(user);
    52. return true;
    53. }
    54. }
    55. //若请求头中不存在Authorization 直接返回false 提示状态码401无权限
    56. response.setStatus(401);
    57. return false;
    58. }
    59. }

    Service解析token实现类
    解析token数据,把token数据解析为user对象
    解析完成后重置缓存时长,将缓存时间重新增加到一个小时

    1. /**
    2. * 解析token,获取user数据信息
    3. * 对token进行检测,如果token存在,则解析出user数据信息
    4. * 如果token不存在,则return null
    5. * 除注册和发送验证码外不需要检测token外,其他功能均需要检测token
    6. */
    7. @Override
    8. public User queryUserByToken(String token) {
    9. try {
    10. // 生成redisKey获取当前登陆人信息
    11. String redisTokenKey = "TOKEN_" + token;
    12. // 获取缓存数据
    13. String cacheData = this.redisTemplate.opsForValue().get(redisTokenKey);
    14. if (StringUtils.isNotEmpty(cacheData)) {
    15. // 刷新时间
    16. this.redisTemplate.expire(redisTokenKey, 1, TimeUnit.HOURS);
    17. //反序列化,将JSON数据转化为user对象返回
    18. return MAPPER.readValue(cacheData, User.class);
    19. }
    20. } catch (Exception e) {
    21. e.printStackTrace();
    22. }
    23. return null;
    24. }

    如果是分布式系统,需要远程调用SSO服务验证token的,可以加上下面的代码
    编码格式和超时时间配置类(远程调用支持类)

    配置 RestTemplate 模板,设置服务远程调用过程中的编码格式

    添加消息数据的字符集格式、连接超时时间和数据获取超时时间

    1. RestTemplateConfig.java
    2. /**
    3. * RestTemplate模板配置类
    4. * @author 陌路
    5. * @date 2022-03-19
    6. * @apiNote 设置模板的字符集、链接超时时间和获取超时时间
    7. */
    8. @Configuration
    9. public class RestTemplateConfig {
    10. @Bean
    11. public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
    12. RestTemplate restTemplate = new RestTemplate(factory);
    13. // 支持中文编码,getMessageConverters消息转换器,将字符集设置为 UTF-8
    14. restTemplate.getMessageConverters()
    15. .set(1, new StringHttpMessageConverter(Charset.forName("UTF-8")));
    16. return restTemplate;
    17. }
    18. @Bean
    19. public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
    20. SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
    21. // 设置数据获取的超时时间,单位为ms
    22. factory.setReadTimeout(5000);
    23. // 设置连接超时时间,单位为ms
    24. factory.setConnectTimeout(5000);
    25. return factory;
    26. }
    27. }

    远程调用Service类

    远程服务调用,验证和获取token数据(调用SSO单点登录中接口)

    url:url为SSO单点登录服务器的IP地址和端口加协议的全名地址

    eg:http://47.101.xxx.xxx:80 本地:eg:http://127.0.0.1:80

    该方法涉及了服务的远程调用过程,需要使用 RestTemplate 中的 .getForObject();

    把上面的service解析token的方法改为下面这段代码

    1. /**
    2. * 解析Token
    3. * 调用SSO接口进行解析token
    4. * @param token
    5. * @return
    6. */
    7. public User queryUserByToken(String token) {
    8. try {
    9. // 返回一串JSON字符串,url为SSO单点登录服务器的ip地址和端口eg:http://127.0.0.1:80
    10. String url = "http://192.168.10.188:18080";
    11. String jsonData = this.restTemplate.getForObject(url + "/user/{token}", String.class, token);
    12. // 如果查询到就返回user对象 查询不到就返回null
    13. if (StringUtils.isNotEmpty(jsonData)) {
    14. // MAPPER.readValue方法将JSON字符串转化为对象 并返回
    15. return MAPPER.readValue(jsonData, User.class);
    16. }
    17. } catch (Exception e) {
    18. e.printStackTrace();
    19. }
    20. return null;
    21. }

    controller控制器接口

    远程服务调用,被调用的接口(SSO单点登录中的控制器接口)

    远程服务调用此接口需要使用全名地址:
    协议://ip地址:端口号 http://ip:port (https://ip:port

    下面的controller作为SSO中被远程调用的测试接口(请根据实际接口调用)

    1. /**
    2. * 验证token
    3. * @param token
    4. * @return
    5. */
    6. @GetMapping("{token}")
    7. public User queryUserByToken(@PathVariable("token") String token) {
    8. //根据token把查询到的结果返回
    9. return this.userService.queryUserByToken(token);
    10. }
  • 相关阅读:
    Spring AOP
    Jetson Orin NX 开发指南(5): 安装 OpenCV 4.6.0 并配置 CUDA 以支持 GPU 加速
    运营-用户分析
    迈动互联中标北京人寿保险,助推客户提升品牌价值
    OpenMLDB + Jupyter Notebook:快速搭建机器学习应用
    紫光同创FPGA实现UDP协议栈带ping功能,基于YT8511和RTL8211,提供2套PDS工程源码和技术支持
    推荐 7 月份 yyds 的开源项目
    几种在ARM MCU上控制流水灯的方法
    LuatOS-SOC接口文档(air780E)-- i2c - I2C操作
    【计算机系统结构期末复习】第六章
  • 原文地址:https://blog.csdn.net/jcc4261/article/details/127816153