Spring Security Jwt Token 自动刷新

网友投稿 1055 2022-09-10

Spring Security Jwt Token 自动刷新

Spring Security Jwt Token 自动刷新

token的自动刷新

​​一、功能需求​​​​二、功能分析​​

​​1、token 的生成​​​​2、token 的自动延长​​​​3、系统资源的保护​​​​4、用户如何传递 token​​

​​三、实现思路​​

​​1、生成 token 和 refreshToken​​​​2、系统 判断 token 是否合法​​

​​四、核心代码如下​​

​​1、过滤器代码,token判断和再次生成​​​​2、jwt 工具类代码​​

​​五、完整代码​​

不推荐在对外的系统中使用下方的这种方式,对内的系统可以使用。对外的系统在请求头只传递token,不传递refreshToken,在token过期时才使用refreshToken换token

一、功能需求

最近项目中有这么一个功能,用户登录系统后,需要给 用户 颁发一个 token ,后续访问系统的请求都需要带上这个 token ,如果请求没有带上这个 token 或者 token 过期了,那么禁止访问系统。如果用户一直访问系统,那么还需要自动延长 token 的过期时间。

二、功能分析

1、token 的生成

使用现在比较流行的 ​​jwt​​ 来生成。

2、token 的自动延长

要实现 token 的自动延长,系统给用户 颁发 一个 token 无法实现,那么通过变通一个,给用户生成 2个 token ,一个用于 api 访问的 token ,一个 用于在 token 过期的时候 用来 刷新 的 refreshToken。并且 refreshToken 的 生命周期要比 token 的生命周期长。

3、系统资源的保护

可以使用​​Spring Security​​ 来保护系统的各种资源。

4、用户如何传递 token

系统中 ​​token​​​ 和 ​​refreshToken​​ 的传递一律放在请求头。

三、实现思路

1、生成 token 和 refreshToken

用户登录系统的时候,后台给用户生成 ​​token​​​ 和 ​​refreshToken​​ 并放在响应头中返回

2、系统 判断 token 是否合法

​​token​​ 未失效的时的处理

​​token​​​ 失效 ,如何使用​​refreshToken​​​来生成新的 ​​token​​

四、核心代码如下

1、过滤器代码,token判断和再次生成

package com.huan.study.security.token;import com.fasterxml.jackson.databind.ObjectMapper;import com.huan.study.security.configuration.TokenProperties;import io.jsonwebtoken.Claims;import io.jsonwebtoken.Jws;import lombok.RequiredArgsConstructor;import lombok.extern.slf4j.Slf4j;import org.apache.commons.lang3.StringUtils;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.org.springframework.org.springframework.stereotype.Component;import org.springframework.web.filter.OncePerRequestFilter;import javax.servlet.FilterChain;import javax.servlet.ServletException;import javax.servlet.javax.servlet.java.io.IOException;import java.nio.charset.StandardCharsets;import java.util.HashMap;import java.util.Map;/** * @author huan 2020-06-07 - 14:34 */@Component@RequiredArgsConstructor(onConstructor = @__(@Autowired))@Slf4jpublic class TokenAuthenticateFilter extends OncePerRequestFilter { private final TokenProperties tokenProperties; private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // 获取 认证头 String authorizationHeader = request.getHeader(tokenProperties.getAuthorizationHeaderName()); if (!checkIsTokenAuthorizationHeader(authorizationHeader)) { log.debug("获取到认证头Authorization的值:[{}]但不是我们系统中登录后签发的。", authorizationHeader); filterChain.doFilter(request, response); return; } // 获取到真实的token String realToken = getRealAuthorizationToken(authorizationHeader); // 解析 jwt token Jws jws = JwtUtils.parserAuthenticateToken(realToken, tokenProperties.getSecretKey()); // token 不合法 if (null == jws) { writeJson(response, "认证token不合法"); return; } // token 是否过期 if (JwtUtils.isJwtExpired(jws)) { // 处理过期 handleTokenExpired(response, request, filterChain); return; } // 构建认证对象 JwtUtils.buildAuthentication(jws, tokenProperties.getUserId()); filterChain.doFilter(request, response); } /** * 处理token过期情况 * * @param response * @param request * @param filterChain * @return * @throws IOException */ private void handleTokenExpired(HttpServletResponse response, HttpServletRequest request, FilterChain filterChain) throws IOException, ServletException { // 获取刷新 token String refreshTokenHeader = request.getHeader(tokenProperties.getRefreshHeaderName()); // 检测 refresh-token 是否是我们系统中签发的 if (!checkIsTokenAuthorizationHeader(refreshTokenHeader)) { log.debug("获取到刷新认证头:[{}]的值:[{}]但不是我们系统中登录后签发的。", tokenProperties.getRefreshHeaderName(), refreshTokenHeader); writeJson(response, "token过期了,refresh token 不是我们系统签发的"); return; } // 解析 refresh-token Jws refreshToken = JwtUtils.parserAuthenticateToken(getRealAuthorizationToken(refreshTokenHeader), tokenProperties.getSecretKey()); // 判断 refresh-token 是否不合法 if (null == refreshToken) { writeJson(response, "refresh token不合法"); return; } // 判断 refresh-token 是否过期 if (JwtUtils.isJwtExpired(refreshToken)) { writeJson(response, "refresh token 过期了"); return; } // 重新签发 token String newToken = JwtUtils.generatorJwtToken( refreshToken.getBody().get(tokenProperties.getUserId()), tokenProperties.getUserId(), tokenProperties.getTokenExpireSecond(), tokenProperties.getSecretKey() ); response.addHeader(tokenProperties.getAuthorizationHeaderName(), newToken); // 构建认证对象 JwtUtils.buildAuthentication(JwtUtils.parserAuthenticateToken(newToken, tokenProperties.getSecretKey()), tokenProperties.getUserId()); filterChain.doFilter(request, response); } /** * 写 json 数据给前端 * * @param response * @throws IOException */ private void writeJson(HttpServletResponse response, String msg) throws IOException { response.setCharacterEncoding(StandardCharsets.UTF_8.name()); response.setContentType(MediaType.APPLICATION_JSON_VALUE); response.setStatus(HttpStatus.UNAUTHORIZED.value()); Map params = new HashMap<>(4); params.put("msg", msg); response.getWriter().print(OBJECT_MAPPER.writeValueAsString(params)); } /** * 获取到真实的 token 串 * * @param authorizationToken * @return */ private String getRealAuthorizationToken(String authorizationToken) { return StringUtils.substring(authorizationToken, tokenProperties.getTokenHeaderPrefix().length()).trim(); } /** * 判断是否是系统中登录后签发的token * * @param authorizationHeader * @return */ private boolean checkIsTokenAuthorizationHeader(String authorizationHeader) { if (StringUtils.isBlank(authorizationHeader)) { return false; } if (!StringUtils.startsWith(authorizationHeader, tokenProperties.getTokenHeaderPrefix())) { return false; } return true; }}

2、jwt 工具类代码

package com.huan.study.security.token;import io.jsonwebtoken.*;import io.jsonwebtoken.impl.DefaultJws;import lombok.AccessLevel;import lombok.NoArgsConstructor;import lombok.extern.slf4j.Slf4j;import org.springframework.security.authentication.TestingAuthenticationToken;import org.springframework.security.core.Authentication;import org.springframework.security.core.context.SecurityContextHolder;import java.time.LocalDateTime;import java.time.ZoneId;import java.util.ArrayList;import java.util.Date;/** * jwt 工具类 * * @author huan * @date 2020-05-20 - 17:09 */@Slf4j@NoArgsConstructor(access = AccessLevel.PRIVATE)public class JwtUtils { /** * 解析 jwt token * * @param token 需要解析的json * @param secretKey 密钥 * @return */ public static Jws parserAuthenticateToken(String token, String secretKey) { try { final Jws claimsJws = Jwts.parser() .setSigningKey(secretKey) .parseClaimsJws(token); return claimsJws; } catch (ExpiredJwtException e) { return new DefaultJws<>(null, e.getClaims(), ""); } catch (UnsupportedJwtException | MalformedJwtException | SignatureException | IllegalArgumentException | IncorrectClaimException e) { log.error(e.getMessage(), e); return null; } } /** * 判断 jwt 是否过期 * * @param jws * @return true:过期 false:没过期 */ public static boolean isJwtExpired(Jws jws) { return jws.getBody().getExpiration().before(new Date()); } /** * 构建认证过的认证对象 */ public static Authentication buildAuthentication(Jws jws, String userIdFieldName) { Object userId = jws.getBody().get(userIdFieldName); TestingAuthenticationToken testingAuthenticationToken = new TestingAuthenticationToken(userId, null, new ArrayList<>(0)); SecurityContextHolder.getContext().setAuthentication(testingAuthenticationToken); return SecurityContextHolder.getContext().getAuthentication(); } /** * 生成 jwt token */ public static String generatorJwtToken(Object loginUserId, String userIdFieldName, Long expireSecond, String secretKey) { Date expireTime = Date.from(LocalDateTime.now().plusSeconds(expireSecond).atZone(ZoneId.systemDefault()).toInstant()); return Jwts.builder() .setHeaderParam("typ", "JWT") .setIssuedAt(new Date()) .setExpiration(expireTime) .claim(userIdFieldName, loginUserId) .signWith(SignatureAlgorithm.HS256, secretKey) .compact(); }}

五、完整代码

​​代码​​ https://gitee.com/huan1993/Spring-Security/tree/master/spring-security-jwt

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:python基础(26):类的成员(字段、方法、属性)
下一篇:win10/win11添加本地普通账户/管理员账户
相关文章

 发表评论

暂时没有评论,来抢沙发吧~