jwt判断token是否过期_SpringSecurity代码实现JWT接口权限授予与校验

1bb1593659aae0fecdf57027dbab8498.png

SpringSecurity代码实现JWT接口权限授予与校验

通过笔者前两篇文章的说明,相信大家已经知道JWT是什么,怎么用,该如何结合Spring Security使用。那么本节就用代码来具体的实现一下JWT登录认证及鉴权的流程。为了大部分的移动端用户观看,本文所有代码均用图片的形式发布,图片点击可放大。有需要源码的朋友可以call我。

一、环境准备工作

  • 建立Spring Boot项目并集成了Spring Security,项目可以正常启动
  • 通过controller写一个HTTP的GET方法服务接口,比如:“/hello”
  • 实现最基本的动态数据验证及权限分配,即实现UserDetailsService接口和UserDetails接口。这两个接口都是向Spring Security提供用户、角色、权限等校验信息的接口
  • 如果你学习过Spring Security的formLogin登录模式,请将HttpSecurity配置中的formLogin()配置段全部去掉。因为JWT完全使用JSON接口,没有from表单提交。
  • HttpSecurity配置中一定要加上csrf().disable(),即暂时关掉跨站攻击CSRF的防御。这样是不安全的,我们后续章节再做处理。

以上的内容,我们在之前的文章中都已经讲过。如果仍然不熟悉,可以翻看本号之前的文章。

二、开发JWT工具类

通过maven坐标引入JWT工具包jjwt

5d922f284e8e39e0e902d19a35eb15f1.png

图片:JWT工具包maven坐标

在application.yml中加入如下自定义一些关于JWT的配置

jwt:  header: JWTHeaderName secret: aabbccdd  expiration: 3600000 
  • 其中header是携带JWT令牌的HTTP的Header的名称。虽然我这里叫做JWTHeaderName,但是在实际生产中可读性越差越安全。
  • secret是用来为JWT基础信息加密和解密的密钥。虽然我在这里在配置文件写死了,但是在实际生产中通常不直接写在配置文件里面。而是通过应用的启动参数传递,并且需要定期修改。
  • expiration是JWT令牌的有效时间。

写一个Spring Boot配置自动加载的工具类。

4a5d66da79232218ba24367401591634.png

图片:开发JWT工具类

上面的代码就是使用io.jsonwebtoken.jjwt提供的方法开发JWT令牌生成、刷新的工具类。

三、开发登录接口(获取Token的接口)

  • "/authentication"接口用于登录验证,并且生成JWT返回给客户端
  • "/refreshtoken"接口用于刷新JWT,更新JWT令牌的有效期
61c7a13a94bbab4e5d3a20170ecf4d6a.png

图片:开发登录接口(获取Token的接口)

核心的token业务逻辑写在JwtAuthService 中

  • login方法中首先使用用户名、密码进行登录验证。如果验证失败抛出BadCredentialsException异常。如果验证成功,程序继续向下走,生成JWT响应给前端
  • refreshToken方法只有在JWT token没有过期的情况下才能刷新,过期了就不能刷新了。需要重新登录。
76327d6cde227d3b7a33dd98e895387d.png

图片:核心的token业务逻辑写在JwtAuthService 中

因为使用到了AuthenticationManager ,所以在继承WebSecurityConfigurerAdapter的SpringSecurity配置实现类中,将AuthenticationManager 声明为一个Bean。并将"/authentication"和 "/refreshtoken" 开放访问权限,如何开放访问权限,我们之前的文章已经讲过了。

@Bean(name = BeanIds.AUTHENTICATION_MANAGER)@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean();}

四、接口访问鉴权过滤器

当用户第一次登陆之后,我们将JWT令牌返回给了客户端,客户端应该将该令牌保存起来。在进行接口请求的时候,将令牌带上,放到HTTP的header里面,header的名字要和jwt.header的配置一致,这样服务端才能解析到。下面我们定义一个拦截器:

  • 拦截接口请求,从请求request获取token,从token中解析得到用户名
  • 然后通过UserDetailsService获得系统用户(从数据库、或其他其存储介质)
  • 根据用户信息和JWT令牌,验证系统用户与用户输入的一致性,并判断JWT是否过期。如果没有过期,至此表明了该用户的确是该系统的用户。
  • 但是,你是系统用户不代表你可以访问所有的接口。所以需要构造UsernamePasswordAuthenticationToken传递用户、权限信息,并将这些信息通过authentication告知Spring Security。Spring Security会以此判断你的接口访问权限。
8c5b079d12477c238cb5b5b47cf5a6d6.png

图片:接口访问鉴权过滤器

在spring Security的配置类(即WebSecurityConfigurerAdapter实现类的configure(HttpSecurity http)配置方法中,加入如下配置:

.sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and().addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
  • 因为我们使用了JWT,表明了我们的应用是一个前后端分离的应用,所以我们可以开启STATELESS禁止使用session。当然这并不绝对,前后端分离的应用通过一些办法也是可以使用session的,这不是本文的核心内容不做赘述。
  • 将我们的自定义jwtAuthenticationTokenFilter,加载到UsernamePasswordAuthenticationFilter的前面。

五、PostMan测试一下:

测试登录接口,即:获取token的接口。输入正确的用户名、密码即可获取token。

89d405aec91e677698a2729374ddb3be.png

图片:测试登录接口


下面我们访问一个我们定义的简单的接口“/hello”,但是不传递JWT令牌,结果是禁止访问。当我们将上一步返回的token,传递到header中,就能正常响应hello的接口结果。

91e3f7734ef9becf7c40bf86be818f35.png

图片:访问接口“/hello”

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Security校验JWT Token代码可以参考如下: 首先,需要创建JWT Token验证过滤器类。该类继承自OncePerRequestFilter,并在doFilterInternal()方法中实现JWT Token校验逻辑: ```java public class JwtTokenAuthenticationFilter extends OncePerRequestFilter { @Autowired private JwtTokenProvider jwtTokenProvider; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String token = jwtTokenProvider.resolveToken(request); try { if (token != null && jwtTokenProvider.validateToken(token)) { Authentication auth = jwtTokenProvider.getAuthentication(token); if (auth != null) { SecurityContextHolder.getContext().setAuthentication(auth); } } } catch (JwtException e) { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); response.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage()); return; } filterChain.doFilter(request, response); } } ``` 然后,需要创建JWT Token的提供者类,该类负责创建Token验证Token: ```java @Component public class JwtTokenProvider { @Value("${jwt.secret}") private String secretKey; @Value("${jwt.token.validity}") private long validityInMilliseconds; private Key getSecretKey() { return Keys.hmacShaKeyFor(secretKey.getBytes()); } public String createToken(String username, List<Role> roles) { Claims claims = Jwts.claims().setSubject(username); claims.put("auth", roles.stream().map(role -> new SimpleGrantedAuthority(role.getAuthority())).filter(Objects::nonNull).collect(Collectors.toList())); Date now = new Date(); Date validity = new Date(now.getTime() + validityInMilliseconds); return Jwts.builder() .setClaims(claims) .setIssuedAt(now) .setExpiration(validity) .signWith(getSecretKey()) .compact(); } public Authentication getAuthentication(String token) { UserDetails userDetails = new User(getUsername(token), "", getAuthorities(token)); return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities()); } private String getUsername(String token) { return Jwts.parserBuilder().setSigningKey(getSecretKey()).build().parseClaimsJws(token).getBody().getSubject(); } private List<GrantedAuthority> getAuthorities(String token) { Claims claims = Jwts.parserBuilder() .setSigningKey(getSecretKey()) .build() .parseClaimsJws(token) .getBody(); List<LinkedHashMap<String, String>> roles = (List<LinkedHashMap<String, String>>) claims.get("auth"); return roles.stream().map(role -> new SimpleGrantedAuthority(role.get("authority"))).collect(Collectors.toList()); } public boolean validateToken(String token) { try { Jwts.parserBuilder().setSigningKey(getSecretKey()).build().parseClaimsJws(token); return true; } catch (JwtException | IllegalArgumentException e) { e.printStackTrace(); return false; } } public String resolveToken(HttpServletRequest req) { String bearerToken = req.getHeader("Authorization"); if (bearerToken != null && bearerToken.startsWith("Bearer ")) { return bearerToken.substring(7); } return null; } } ``` 最后,需要把上述过滤器类和提供者类添加到Spring Security的配置中: ```java @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private JwtTokenProvider jwtTokenProvider; @Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable() .authorizeRequests() .antMatchers("/api/v1/auth/login").permitAll() .anyRequest().authenticated() .and() .addFilterBefore(new JwtTokenAuthenticationFilter(jwtTokenProvider), UsernamePasswordAuthenticationFilter.class); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(12); } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder()); } @Bean public UserDetailsService userDetailsService() { return new UserServiceImpl(); } } ``` 以上代码用来实现Spring Security校验JWT Token的功能,提供了创建Token校验Token和获取Token中存储的用户和权限信息等相关方法。您可以根据您的实际需求进行修改和调整。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值