SpringMVC-登录校验

news/2025/3/19 14:13:51/

文章目录

  • 1.会话技术
    • 1.1 Cookie
    • 1.2 Session
    • 1.3 JWT
      • 1.3.1 JWT简介
      • 1.3.2 JWT结构
      • 1.3.3 使用JWT
  • 2.拦截技术
    • 2.1 过滤器(Filter)
      • 2.1.1 快速上手
      • 2.1.2 执行流程
      • 2.1.3 拦截路径
      • 2.1.4 过滤器链
    • 2.2 拦截器(Interceptor)
      • 2.2.1 快速上手
      • 2.2.2 执行流程
      • 2.2.3 拦截路径
      • 2.2.4 拦截器链
  • 3.登入认证(案例)

1.会话技术

  • 前因:HTTP是无状态协议,每次请求都是独立的,服务器不会自动保存任何前一次请求的上下文信息,无法满足需要“记住”用户状态的场景(如登录),需要通过会话技术(Cookie、Session、JWT)实现状态管理。

1.1 Cookie

  • cookie是客户端会话跟踪技术它是存储在客户端浏览器的,我们使用 cookie 来跟踪会话,我们就可以在浏览器第一次发起请求来请求服务器的时候,我们在服务器端来设置一个cookie。

    比如第一次请求了登录接口,登录接口执行完成之后,我们就可以设置一个cookie,在 cookie 当中我们就可以来存储用户相关的一些数据信息。比如我可以在 cookie 当中来存储当前登录用户的用户名,用户的ID。

    服务器端在给客户端在响应数据的时候,会自动的将 cookie 响应给浏览器,浏览器接收到响应回来的 cookie 之后,会自动的将 cookie 的值存储在浏览器本地。接下来在后续的每一次请求当中,都会将浏览器本地所存储的 cookie 自动地携带到服务端。

    在这里插入图片描述

    刚才在介绍流程的时候,用了 3 个自动:

    • 服务器会 自动 的将 cookie 响应给浏览器。

    • 浏览器接收到响应回来的数据之后,会 自动 的将 cookie 存储在浏览器本地。

    • 在后续的请求当中,浏览器会 自动 的将 cookie 携带到服务器端。

    为什么这一切都是自动化进行的?

    是因为 cookie 它是 HTP 协议当中所支持的技术,而各大浏览器厂商都支持了这一标准。在 HTTP 协议官方给我们提供了一个响应头和请求头:

    • 响应头 Set-Cookie :设置Cookie数据的
    • 请求头 Cookie:携带Cookie数据的

    在这里插入图片描述

    代码实现:

java">@Slf4j
@RestController
public class SessionController {//设置Cookie@GetMapping("/c1")public Result cookie1(HttpServletResponse response){response.addCookie(new Cookie("login_username","itheima")); //设置Cookie/响应Cookiereturn Result.success();}//获取Cookie@GetMapping("/c2")public Result cookie2(HttpServletRequest request){Cookie[] cookies = request.getCookies();for (Cookie cookie : cookies) {if(cookie.getName().equals("login_username")){System.out.println("login_username: "+cookie.getValue()); //输出name为login_username的cookie}}return Result.success();}
}   

A. 访问c1接口,设置Cookie,http://localhost:8080/c1

在这里插入图片描述

我们可以看到,设置的cookie,通过响应头Set-Cookie响应给浏览器,并且浏览器会将Cookie,存储在浏览器端。

在这里插入图片描述

B. 访问c2接口 http://localhost:8080/c2,此时浏览器会自动的将Cookie携带到服务端,是通过请求头Cookie,携带的。

在这里插入图片描述


优缺点

  • 优点:HTTP协议中支持的技术(像Set-Cookie 响应头的解析以及 Cookie 请求头数据的携带,都是浏览器自动进行的,是无需我们手动操作的)。
  • 缺点:
    • 移动端APP(Android、IOS)中无法使用Cookie。
    • 不安全,用户可以自己禁用Cookie。
    • Cookie不能跨域

跨域介绍:

在这里插入图片描述

  • 现在的项目,大部分都是前后端分离的,前后端最终也会分开部署,前端部署在服务器 192.168.150.200 上,端口 80,后端部署在 192.168.150.100上,端口 8080
  • 我们打开浏览器直接访问前端工程,访问url:http://192.168.150.200/login.html
  • 然后在该页面发起请求到服务端,而服务端所在地址不再是localhost,而是服务器的IP地址192.168.150.100,假设访问接口地址为:http://192.168.150.100:8080/login
  • 那此时就存在跨域操作了,因为我们是在 http://192.168.150.200/login.html 这个页面上访问了http://192.168.150.100:8080/login 接口
  • 此时如果服务器设置了一个Cookie,这个Cookie是不能使用的,因为Cookie无法跨域

区分跨域的维度:

  • 协议
  • IP/协议
  • 端口

只要上述的三个维度有任何一个维度不同,那就是跨域操作

举例:

​ http://192.168.150.200/login.html ----------> https://192.168.150.200/login [协议不同,跨域]

​ http://192.168.150.200/login.html ----------> http://192.168.150.100/login [IP不同,跨域]

​ http://192.168.150.200/login.html ----------> http://192.168.150.200:8080/login [端口不同,跨域]

​ http://192.168.150.200/login.html ----------> http://192.168.150.200/login [不跨域]

1.2 Session

  • Session是服务端会话技术,所以它是存储在服务器端的。而 Session 的底层其实就是基于我们刚才所介绍的 Cookie 来实现的。

    1. 创建Session:如果是第一次请求Session ,会话对象是不存在的,这个时候服务器会自动的创建一个会话对象Session 。而每一个会话对象Session ,它都有一个ID(示意图中Session后面括号中的1,就表示ID),我们称之为 Session 的ID。

      在这里插入图片描述

    2. 响应Cookie (JSESSIONID):接下来,服务器端在给浏览器响应数据的时候,它会将 Session 的 ID 通过 Cookie 响应给浏览器。其实在响应头当中增加了一个 Set-Cookie 响应头。这个 Set-Cookie 响应头对应的值是不是cookie? cookie 的名字是固定的 JSESSIONID 代表的服务器端会话对象 Session 的 ID。浏览器会自动识别这个响应头,然后自动将Cookie存储在浏览器本地。

      在这里插入图片描述

    3. 查找Session:接下来,在后续的每一次请求当中,都会将 Cookie 的数据获取出来,并且携带到服务端。接下来服务器拿到JSESSIONID这个 Cookie 的值,也就是 Session 的ID。拿到 ID 之后,就会从众多的 Session 当中来找到当前请求对应的会话对象Session。

      在这里插入图片描述


      代码实现:

      java">@Slf4j
      @RestController
      public class SessionController {@GetMapping("/s1")public Result session1(HttpSession session){log.info("HttpSession-s1: {}", session.hashCode());session.setAttribute("loginUser", "tom"); //往session中存储数据return Result.success();}@GetMapping("/s2")public Result session2(HttpServletRequest request){HttpSession session = request.getSession();log.info("HttpSession-s2: {}", session.hashCode());Object loginUser = session.getAttribute("loginUser"); //从session中获取数据log.info("loginUser: {}", loginUser);return Result.success(loginUser);}
      }
      

优缺点

  • 优点:Session是存储在服务端的,安全

  • 缺点:

    • 每个用户的登录信息都会保存到服务端器中,当用户增多将会增加服务端压力。
    • 服务器集群环境下无法直接使用Session
    • 对于非浏览器端包括移动端APP(Android、IOS)中无法使用Cookie
    • 用户可以自己禁用Cookie,Cookie如果被截获也有安全风险
    • Cookie不能跨域

PS:Session 底层是基于Cookie实现的会话跟踪,如果Cookie不可用,则该方案,也就失效了。所以大多数情况使用下述介绍的JWT进行用户认证。

1.3 JWT

1.3.1 JWT简介

  1. 定义:JWT(Json Web Token)通过数字签名的方式,以Json为载体,定义了一种简洁的、自包含的,用于在通信双方安全传输信息的技术。

  2. 工作流程

    1. 用户登录:客户端使用用户名和密码请求登录,服务端收到请求,验证用户名和密码。
    2. 生成JWT:登录成功,服务端生成保护用户关键信息的JWT返回给前端。
    3. 保存JWT:客户端接收到JWT后将JWT存储起来,后续客户端每次向服务端请求资源时需要携带服务端签发的JWT,可以在cookie或者header中携带。退出登录时删除保存的JWT。
    4. 校验JWT:服务端统一拦截请求,校验JWT(有效性、时效性、方法签名等)。
    5. 解析JWT:后端解析出JWT中包含的用户信息,根据用户信息进行权限校验、数据处理等相关操作,返回结果给前端。
  3. 优势:

    1. 支持跨域访问,解决了cookie无法跨域的问题,跨域后不会存在信息丢失问题,适用于分布式微服务。
    2. 减轻服务端压力,JWT本身包含了登录用户的信息,服务端无需刻意存储JWT。
    3. 适用性广,当客户端是非浏览器平台时,cookie是不被支持的,而JWT支持大多数web应用。
    4. 安全性高,通过非对称加密算法和数字签名技术,防止JWT被篡改。
    5. JWT基于Json,方便解析和拓展,可以在令牌中轻松的添加自定义内容。
  4. 劣势:

    1. JWT不支持会话管理,一旦签发无法撤回或修改,也不能主动使令牌失效,除非到了过期时间。
    2. JWT中的信息可以在客户端解码,因此敏感信息不应该存储在JWT中

1.3.2 JWT结构

  • JWT由Header(标头),PayLoad (负载),Signature(签名)三个部分组成,通过Base64编码方式分别转换成字符串,三个字符串之间使用.的点来分割。

    JWTString=Base64(Header).Base64(Payload).HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)
    
  1. Header(标头):包含令牌类型签名算法,是一个描述JWT元数据的JSON对象,alg属性表示签名使用的算法,默认为HMAC SHA256(写为HS256);typ属性表示令牌的类型,JWT令牌统一写为JWT。

    {"alg": "HS256","typ": "JWT"
    }
    
  2. PayLoad(负载):有效载荷部分,是JWT的主体内容部分,也是一个JSON对象,包含需要传递的数据。 JWT指定七个默认字段供选择,除了默认字段外可以自定义私有字段(一般是包含用户信息的字段)。

    iss:发行人
    exp:到期时间
    sub:主题
    aud:用户
    nbf:在此之前不可用
    iat:发布时间
    jti:JWT ID用于标识该JWT
    
    {"sub": "1234567890","name": "Helen","admin": true
    }
    
  3. Signature(签名)防止JWT被篡改、确保安全性,需要使用Base64编码后的header和payload数据,通过指定的算法生成哈希。首先,需要指定一个密钥(secret)。该密钥仅仅为保存在服务器中,并且不能向用户公开。然后,使用header中指定的签名算法(默认情况下为HMAC SHA256)根据以下公式生成签名:

    HMACSHA256(base64UrlEncode(header)+"."+base64UrlEncode(payload),secret)
    

    在计算出签名哈希后,JWT头,有效载荷和签名哈希的三个部分组合成一个字符串,每个部分用.分隔,就构成整个JWT对象

    在这里插入图片描述


  • JWT各个部分的作用:

    在服务端接收到客户端发送过来的JWT token之后:

    • header和payload可以直接利用base64解码出原文,从header中获取哈希签名的算法,从payload中获取有效数据。
    • signature由于使用了不可逆的加密算法,无法解码出原文它的作用是校验token有没有被篡改。服务端获取header中的加密算法之后,利用该算法加上secretKey对header、payload进行加密,比对加密后的数据和客户端发送过来的是否一致。注意secretKey只能保存在服务端,而且对于不同的加密算法其含义有所不同,一般对于MD5类型的摘要加密算法,secretKey实际上代表的是盐值。
  • JWT是如何将原始的JSON格式数据,转变为字符串的呢?

    其实在生成JWT令牌时,会对JSON格式的数据进行一次编码:进行base64编码

    Base64:是一种基于64个可打印的字符来表示二进制数据的编码方式。既然能编码,那也就意味着也能解码。所使用的64个字符分别是A到Z、a到z、 0- 9,一个加号,一个斜杠,加起来就是64个字符。任何数据经过base64编码之后,最终就会通过这64个字符来表示。当然还有一个符号,那就是等号。等号它是一个补位的符号

    需要注意的是Base64是编码方式,而不是加密方式

  • 到目前为止,jwt的签名算法有三种:

    1. HMAC【哈希消息验证码(对称)】:HS256/HS384/HS512
    2. RSASSA【RSA签名算法(非对称)】(RS256/RS384/RS512)
    3. ECDSA【椭圆曲线数据签名算法(非对称)】(ES256/ES384/ES512)

1.3.3 使用JWT

  1. 引入依赖:

    java"><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version>
    </dependency>
    
  2. JWT工具类:

    java">public class JwtUtil {/*** 生成jwt* 使用Hs256算法, 私匙使用固定秘钥,可将密钥存储在服务端** @param secretKey jwt秘钥* @param ttlMillis jwt过期时间(毫秒)* @param claims    负载* @return*/public static String createJWT(String secretKey, long ttlMillis, Map<String, Object> claims) {// 指定签名的时候使用的签名算法,也就是header那部分SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;// 生成JWT的时间long expMillis = System.currentTimeMillis() + ttlMillis;Date exp = new Date(expMillis);// 设置jwt的bodyString jwt = Jwts.builder()// 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的.setClaims(claims)// 设置签名使用的签名算法和签名使用的秘钥.signWith(signatureAlgorithm, secretKey.getBytes(StandardCharsets.UTF_8))// 设置过期时间.setExpiration(exp).compact();return jwt;}/*** Token解密** @param secretKey jwt秘钥 此秘钥一定要保留好在服务端, 不能暴露出去, 否则signature就可以被伪造, 如果对接多个客户端建议改造成多个* @param token     加密后的token* @return*/public static Claims parseJWT(String secretKey, String token) {// 得到DefaultJwtParserClaims claims = Jwts.parser()// 设置签名的秘钥.setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8))// 设置需要解析的jwt.parseClaimsJws(token).getBody();return claims;}
    }
    

2.拦截技术

  • 如果需要在服务端统一拦截指定请求从而实现一些特殊的功能,就需要用到拦截技术,其中主要包含过滤器(Filter)拦截器(Interceptor)

  • 常见场景:登录校验、统一编码处理、敏感字符处理等。

  • 拦截器和过滤器之间的区别:

    1. 归属不同:Filter属于Servlet技术,Interceptor属于SpringMVC技术。
    2. 拦截范围不同:Filter对所有访问进行增强,Interceptor仅针对SpringMVC的访问进行增强。
    3. 接口规范不同:过滤器需要实现Filter接口,而拦截器需要实现HandlerInterceptor接口。

2.1 过滤器(Filter)

  • Filter是 JavaWeb三大组件(Servlet、Filter、Listener)之一。

    在这里插入图片描述

2.1.1 快速上手

下面我们通过Filter快速入门程序掌握过滤器的基本使用操作:

  1. 定义过滤器:定义一个类,实现 Filter 接口,并重写其所有方法,并在Filter类上加 @WebFilter 注解,同时配置拦截资源的路径。

    java">@WebFilter(urlPatterns = "/*") //配置过滤器要拦截的请求路径( /*) 
    public class DemoFilter implements Filter { //初始化方法, 只调用一次@Override public void init(FilterConfig filterConfig) throws ServletException {System.out.println("DemoFilter.init初始化方法执行了");}//拦截到请求之后调用, 调用多次@Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {System.out.println("DemoFilter执行放行前逻辑");chain.doFilter(request,response); //放行操作,如果不放行无法访问后续资源System.out.println("DemoFilter执行放行后逻辑");}//销毁方法, 只调用一次@Override public void destroy() {System.out.println("DemoFilter.destroy销毁方法执行了");}
    }
    
  2. SpringBoot启动类上加上 @ServletComponentScan 开启Servlet组件支持。

    java">@ServletComponentScan
    @SpringBootApplication
    public class TliasWebManagementApplication {public static void main(String[] args) {SpringApplication.run(TliasWebManagementApplication.class, args);}
    }
    
  3. 登录校验过滤器案例

    java">@Slf4j
    @WebFilter(urlPatterns = "/*") //拦截所有请求
    public class LoginCheckFilter implements Filter {@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {//前置:强制转换为http协议的请求对象、响应对象 (转换原因:要使用子类中特有方法)HttpServletRequest request = (HttpServletRequest) servletRequest;HttpServletResponse response = (HttpServletResponse) servletResponse;//1.获取请求urlString url = request.getRequestURL().toString();log.info("请求路径:{}", url); //请求路径:http://localhost:8080/login//2.判断请求url中是否包含login,如果包含,说明是登录操作,放行if(url.contains("/login")){chain.doFilter(request, response);//放行请求return;//结束当前方法的执行}//3.获取请求头中的令牌(token)String token = request.getHeader("token");log.info("从请求头中获取的令牌:{}",token);//4.判断令牌是否存在,如果不存在,返回错误结果(未登录)if(!StringUtils.hasLength(token)){log.info("Token不存在");Result responseResult = Result.error("NOT_LOGIN");//把Result对象转换为JSON格式字符串 (fastjson是阿里巴巴提供的用于实现对象和json的转换工具类)String json = JSONObject.toJSONString(responseResult);response.setContentType("application/json;charset=utf-8");//响应response.getWriter().write(json);return;}//5.解析token,如果解析失败,返回错误结果(未登录)try {JwtUtils.parseJWT(token);}catch (Exception e){log.info("令牌解析失败!");Result responseResult = Result.error("NOT_LOGIN");//把Result对象转换为JSON格式字符串 (fastjson是阿里巴巴提供的用于实现对象和json的转换工具类)String json = JSONObject.toJSONString(responseResult);response.setContentType("application/json;charset=utf-8");//响应response.getWriter().write(json);return;}//6.放行chain.doFilter(request, response);}
    }
    

2.1.2 执行流程

首先我们先来看下过滤器的执行流程:

在这里插入图片描述

过滤器当中我们拦截到了请求之后,如果希望继续访问后面的web资源,就要执行放行操作,放行就是调用 FilterChain对象当中的doFilter()方法,在调用doFilter()这个方法之前所编写的代码属于放行之前的逻辑。

在放行后访问完 web 资源之后还会回到过滤器当中,回到过滤器之后如有需求还可以执行放行之后的逻辑,放行之后的逻辑我们写在doFilter()这行代码之后。

2.1.3 拦截路径

Filter可以根据需求,配置不同的拦截资源路径:

拦截路径urlPatterns值含义
拦截具体路径/login只有访问 /login 路径时,才会被拦截
目录拦截/emps/*访问/emps下的所有资源,都会被拦截
拦截所有/*访问所有资源,都会被拦截

2.1.4 过滤器链

在一个web应用程序当中,可以配置多个过滤器,多个过滤器就形成了一个过滤器链。以注解方式配置的Filter过滤器,它的执行优先级是按时过滤器类名的自动排序确定的,类名排名越靠前,优先级越高

在这里插入图片描述

2.2 拦截器(Interceptor)

  • 拦截器是Spring框架中提供的,用来动态拦截控制器方法的执行,在指定方法调用前后,根据业务需要执行预先设定的代码。

2.2.1 快速上手

下面我们通过快速入门程序,来学习下拦截器的基本使用。拦截器的使用步骤和过滤器类似,也分为两步:

  1. 定义拦截器:实现HandlerInterceptor接口,并重写其所有方法。

    java">//自定义拦截器
    @Component
    public class LoginCheckInterceptor implements HandlerInterceptor {//目标资源方法执行前执行。 返回true:放行    返回false:不放行@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("LoginCheckInterceptor.preHandle .... ");return true; //true表示放行}//目标资源方法执行后执行@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("LoginCheckInterceptor.postHandle ... ");}//视图渲染完毕后执行,最后执行@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("LoginCheckInterceptor.afterCompletion .... ");}
    }
    
  2. 注册配置拦截器:实现WebMvcConfigurer接口,并重写addInterceptors方法。

    java">@Configuration  
    public class WebConfig implements WebMvcConfigurer {//自定义的拦截器对象@Autowiredprivate LoginCheckInterceptor loginCheckInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {//注册自定义拦截器对象registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/**");//设置拦截器拦截的请求路径( /** 表示拦截所有请求)}
    }
    

2.2.2 执行流程

介绍完拦截路径的配置之后,接下来我们再来介绍拦截器的执行流程。通过执行流程,大家就能够清晰的知道过滤器与拦截器的执行时机。

在这里插入图片描述

在这里插入图片描述

  • 当我们打开浏览器来访问部署在web服务器当中的web应用时,此时我们所定义的过滤器会拦截到这次请求。拦截到这次请求之后,它会先执行放行前的逻辑,然后再执行放行操作。而由于我们当前是基于springboot开发的,所以放行之后是进入到了spring的环境当中,也就是要来访问我们所定义的controller当中的接口方法。

  • Tomcat并不识别所编写的Controller程序,但是它识别Servlet程序,所以在Spring的Web环境中提供了一个非常核心的Servlet:DispatcherServlet(前端控制器),所有请求都会先进行到DispatcherServlet,再将请求转给Controller。

  • 当我们定义了拦截器后,会在执行Controller的方法之前,请求被拦截器拦截住。执行preHandle()方法,这个方法执行完成后需要返回一个布尔类型的值,如果返回true,就表示放行本次操作,才会继续访问controller中的方法;如果返回false,则不会放行(controller中的方法也不会执行)。

  • 在controller当中的方法执行完毕之后,再回过来执行postHandle()这个方法以及afterCompletion() 方法,然后再返回给DispatcherServlet,最终再来执行过滤器当中放行后的这一部分逻辑的逻辑。执行完毕之后,最终给浏览器响应数据。

2.2.3 拦截路径

  • 通过addPathPatterns("要拦截路径")方法,就可以指定要拦截哪些资源,调用excludePathPatterns("不拦截路径")方法,指定哪些资源不需要拦截。

    在拦截器中除了可以设置/**拦截所有资源外,还有一些常见拦截路径设置:

    拦截路径含义举例
    /*一级路径能匹配/depts,/emps,/login,不能匹配 /depts/1
    /**任意级路径能匹配/depts,/depts/1,/depts/1/2
    /depts/*/depts下的一级路径能匹配/depts/1,不能匹配/depts/1/2,/depts
    /depts/**/depts下的任意级路径能匹配/depts,/depts/1,/depts/1/2,不能匹配/emps/1

2.2.4 拦截器链

在一个Spring应用程序当中,可以配置多个拦截器,多个拦截器就形成了一个拦截器链,拦截器链的运行顺序参照拦截器添加顺序为准

在这里插入图片描述

preHandle:与配置顺序相同,必定运行

postHandle:与配置顺序相反,可能不运行

afterCompletion:与配置顺序相反,可能不运行

3.登入认证(案例)

  • 在实际的SpringBoot项目中,一般我们可以用如下流程做登录:

    1. 在登录验证通过后,给用户生成一个对应的随机token(注意这个token不是指jwt,可以用uuid等算法生成),然后将这个token作为key的一部分,用户信息作为value存入Redis,并设置过期时间,这个过期时间就是登录失效的时间。

    2. 将第1步中生成的随机token作为JWT的payload生成JWT字符串返回给前端。

    3. 前端之后每次请求都在请求头中的Authorization字段中携带JWT字符串。

    4. 后端定义一个拦截器,每次收到前端请求时,都先从请求头中的Authorization字段中取出JWT字符串并进行验证,验证通过后解析出payload中的随机token,然后再用这个随机token得到key,从Redis中获取用户信息,如果能获取到就说明用户已经登录。

      下述代码未使用redis暂存用户信息,只做简单示范

      java">@Component
      @Slf4j
      public class JwtTokenPatientInterceptor implements HandlerInterceptor {@Autowiredprivate JwtProperties jwtProperties;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//判断当前拦截到的是Controller的方法还是其他资源if (!(handler instanceof HandlerMethod)) {//当前拦截到的不是动态方法,直接放行return true;}// 1.从请求头中获取令牌String token = request.getHeader(jwtProperties.getUserTokenName());// 2.判断令牌是否存在,如果不存在,返回错误结果(未登录)if (StringUtils.isNull(token)) {log.info("请求头token为空,返回未登录的信息");response.setStatus(401);return false;}// 3.校验令牌try {log.info("jwt校验:{}", token);Claims claims = JwtUtil.parseJWT(jwtProperties.getUserSecretKey(), token);Long patientId = Long.valueOf(claims.get(JwtClaimsConstant.PATIENT_ID).toString());log.info("当前用户的id:" + patientId);BaseContext.setCurrentId(patientId);//3、通过,放行return true;} catch (Exception ex) {//4、不通过,响应401状态码response.setStatus(401);return false;}}
      }
      
      java">@Component
      @ConfigurationProperties(prefix = "jwt")
      @Data
      public class JwtProperties {private String userSecretKey; //密钥private long userTtl; //过期时间private String userTokenName; //token名称
      }
      

      在实际开发中需要用下列手段来增加JWT的安全性:

      1. 因为JWT是在请求头中传递的,所以为了避免网络劫持,推荐使用HTTPS来传输,更加安全。
      2. JWT的哈希签名的密钥是存放在服务端的,所以只要服务器不被攻破,理论上JWT是安全的,因此要保证服务器的安全。
      3. JWT可以使用暴力穷举来破解,所以为了应对这种破解方式,可以定期更换服务端的哈希签名密钥(相当于盐值)。这样可以保证等破解结果出来了,你的密钥也已经换了。

参考博客

  1. JWT详解

http://www.ppmy.cn/news/1580332.html

相关文章

【JavaScript】三、数据类型

文章目录 1、数据类型1.1 基本数据类型&#xff1a;数字型number1.2 基本数据类型&#xff1a;字符串类型1.3 基本数据类型&#xff1a;布尔类型1.4 基本数据类型&#xff1a;未定义undefined1.5 基本数据类型&#xff1a;空类型null 2、查看数据类型2.1 从控制台看2.2 typeof …

爬虫逆向:详细讲述iOS底层原理及机制

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 1. iOS 系统架构1.1 Core OS 层1.2 Core Services 层1.3 Media 层1.4 Cocoa Touch 层2. iOS 的核心机制2.1 应用生命周期2.2 内存管理2.3 多线程2.4 文件系统2.5 网络通信3. iOS 的启动流程4. iOS 的安全机制4.1 代码签…

银行数字化转型

CNAPS 中国现代化支付系统 China National Automatic Payment System CIPS 人民币跨境支付系统 Cross-border Interbank Payment System 现代银行体系形成了中央银行这个特殊的银行类型&#xff0c;由中央银行居中建立清算体系是目前多数国家采用的金融清算体系构建方式&#…

(七)Spring Boot学习——Redis使用

有部分内容是常用的&#xff0c;为了避免每次都查询数据库&#xff0c;将部分数据存入Redis。 一、 下载并安装 Redis Windows 版的 Redis 官方已不再维护&#xff0c;你可以使用 微软提供的 Redis for Windows 版本 或者 使用 WSL&#xff08;Windows Subsystem for Linux&a…

基于Spring Boot的红色革命文物征集管理系统的设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

蓝桥云客 快速幂

1.快速幂 - 蓝桥云课 题目描述 输入 b&#xff0c;p&#xff0c;k 的值&#xff0c;求 bpmodk 的值。其中 2≤b,p,k≤109。 输入描述 三个整数 b,p,k。 输出描述 输出 bpmodks&#xff0c;s 为运算结果。 输入输出样例 示例 输入输出2 10 97 运行限制 最大运行时间&…

C# 事件(Event)核心概念

文章目录 前言‌1. 事件的核心概念‌‌2. 事件的声明与使用‌‌3. 标准事件模式&#xff08;EventHandler 和 EventArgs&#xff09;‌4. 事件与委托的区别‌‌5. 事件的使用场景‌‌6. 高级特性‌‌7. 注意事项‌‌8. 完整示例&#xff1a;温度监控系统‌‌9. 常见问题‌ 前言…

PSI5接口

文章目录 前言PSI5接口简介操作模式命名规则异步操作模式&#xff08;PSI5-A&#xff09;同步操作模式&#xff08;PSI5-P&#xff09; 传感器->ECU物理层&#xff08;位编码&#xff09;数据链路层数据帧帧格式串行消息帧10bits 传感器帧定义超10bits传感器帧定义 ECU->…