这篇文章主要讲解了“JTW怎么实现认证与授权”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“JTW怎么实现认证与授权”吧!
JWT理论部分
摘要
是为了在网络应用环境中传递声明而执行的一种基于json的开放标准。特别适合分布式站点的单点登录场景。jwt的声明一般被用来在身份提供者和服务提供者之间传递被认证的用户身份信息,以便于从服务器获取资源,也可以增加一些额外的其他业务逻辑所用到的生命信息。
组成
-
header:头部
alg表示签名的生成算法。 typ表示这个令牌(token)的类型(type),JWT 令牌统一写为JWT。
{ "alg":"HS256", "typ":"JWT" }
-
payload:有效载荷
可选参数: iss: 签发者 sub: 面向用户 aud: 接收者 iat(issued at): 签发时间 exp(expires): 过期时间 nbf(not before):不能被接收处理时间,在此之前不能被接收处理 jti:JWT ID为web token提供唯一标识
{ "sub":"123456", "exp":"1564641412" }
-
signature:签名
以header和payload生成的签名,防止header和payload被篡改。这里需要指定一个密码(secret)。该密码仅仅为保存在服务器中,并且不能向用户公开, 解析时需要匹配密码(secret)才能解析成功。
String signature = HMACSHA512(base64UrlEncode(header)+"."+ base64UrlEncode(payload), secret);
JWT 实践
pom文件引入相关依赖
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.0</version> </dependency>
配置拦截器
需拦截除登录外所有接口,并开放静态路径
@Configuration public class WebConfig extends WebMvcConfigurationSupport { @Autowired private JwtInterceptor jwtInterceptor; /** * 配置静态资源 * @param registry */ @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/**") .addResourceLocations("classpath:/static/"); registry.addResourceHandler("/swagger-ui.html") .addResourceLocations("classpath:/META-INF/resources/"); registry.addResourceHandler("/webjars/**") .addResourceLocations("classpath:/META-INF/resources/webjars/"); super.addResourceHandlers(registry); } /** * 注册拦截器 * @param registry */ @Override public void addInterceptors(InterceptorRegistry registry) { // 添加拦截的请求,并排除几个不拦截的请求 registry.addInterceptor(jwtInterceptor).addPathPatterns("/**") .excludePathPatterns("/loginController/loginSystem","/swagger-ui.html", "/static/**","/swagger-resources/**","/webjars/**","); }
添加配置请求拦截器
@Component public class JwtInterceptor extends HandlerInterceptorAdapter { @Autowired private JwtTokenConfig jwtTokenConfig; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String header = Constant.getContentProperties().getStringProperty("header"); //获取请求token String reqToken = getJwt(request); if (jwtTokenConfig.validateJwtToken(reqToken)) { return true; } return false; } /** * 获取并返回token信息 * @param request * @return */ private String getJwt(HttpServletRequest request) { //从header中获取 String authHeader = request.getHeader("Authorization"); String header = Constant.getContentProperties().getStringProperty("header"); if (StringUtils.startsWith(authHeader, header)) { authHeader = authHeader.replace(header+" ", ""); } return authHeader; }
添加JWT配置类
配置类用于生成、解析、校验Token。由于业务需求,token过期后将派发新的token。
@Configuration public class JwtTokenConfig { private static final Logger logger = LoggerFactory.getLogger(JwtTokenConfig.class); @Autowired private JwtTokenMapper jwtTokenMapper; //读取配置文件信息 private String expire = Constant.getContentProperties().getStringProperty("expire"); private String secret = Constant.getContentProperties().getStringProperty("secret"); private String header = Constant.getContentProperties().getStringProperty("header"); /** * 生成token * @param subject * @return */ public String generateToken(String subject) { //设置过期时间 Date expireDate = new Date(System.currentTimeMillis() + 1000 * Long.parseLong(expire)); //组建header Map<String, Object> headMap = new HashMap<>(); headMap.put("alg", SignatureAlgorithm.HS256.getValue()); headMap.put("typ", "JWT"); //自定义组件 Map<String, Object> claims = new HashMap<>(); claims.put("created", DateUtil.formatDate(new Date(),DateUtil.YEAR_TO_SECOND)); String access_token = Jwts.builder().setHeader(headMap) .signWith(SignatureAlgorithm.HS512, secret) .setClaims(claims) //面向用户 .setSubject(subject) //过期时间 .setExpiration(expireDate) //生成token .compact(); //刷新数据库中token信息 refreshToken(subject, access_token, expireDate); logger.info(subject+" 生成后的token:"+access_token); return access_token; } /** * 获得签名信息 * @param token * @return */ public Claims getClaimsFromToken(String token) { Claims claims = null; try { claims = Jwts.parser() .setSigningKey(secret) .parseClaimsJws(token) .getBody(); logger.info("解析token结果:"+claims); } catch (ExpiredJwtException e) { //token过期创建并返回一个新token logger.info("token过期,正在创建新token\n"); JwtToken jwtToken = jwtTokenMapper.selectByToken(token); if(jwtToken != null){ String newToken = generateToken(jwtToken.getUserCode()); claims = Jwts.parser() .setSigningKey(secret) .parseClaimsJws(newToken) .getBody(); logger.info("解析token结果:"+claims); } else{ logger.info("token无效"); } } catch (Exception e){ logger.info("解析异常"); } return claims; } /** * token校验 * @param token * @return */ public boolean validateJwtToken(String token){ if(StringUtils.isNotBlank(token)){ //获得token中signature信息,并验证过期时间 Claims claims = getClaimsFromToken(token); JwtToken jwtToken = jwtTokenMapper.selectByUserCode(claims.getSubject()); HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse(); if(jwtToken == null || jwtToken.getJwtId() <= 0){ logger.info("token无效"); } //返回信息中将token添加至header中 response.setHeader("Authorization",header+" "+jwtToken.getToken()); return true; } else{ logger.info("token为空"); } } /** * 刷新数据库中token信息 * @param subject * @param access_token * @param expireDate */ public void refreshToken(String subject, String access_token, Date expireDate){ //数据库中存储数据 jwtTokenMapper.deleteByUserCode(subject); JwtToken jwtToken = new JwtToken(); jwtToken.setUserCode(subject); jwtToken.setToken(access_token); jwtToken.setCreateTime(new Date()); jwtToken.setExpireTime(expireDate); jwtTokenMapper.insertSelective(jwtToken); } }
统一处理拦截器中异常
拦截器中出现异常后,通常会使用系统默认方式返回,若想自定义返回json,则需要使用异常拦截器。
@ControllerAdvice public class ExceptionInterceptor { @ExceptionHandler(value = Exception.class) @ResponseBody public Map<String, Object> allExceptionHandler(Exception e){ Map<String, Object> resultMap = new HashMap<>(2); if(e instanceof Exception){ resultMap.put("code", "500"); resultMap.put("msg", e.getMessage()); } return resultMap; } }