什么是JWT
广义上讲JWT是一个标准的名称;狭义上讲JWT指的就是用来传递的那个token字符串。 JWT的组成JWT含有三个部分:
头部(header) { 'typ':'JWT', 'alg':'HS256'} 载荷(payload)
签证(signature)
JWT特点
如何使用JWT?在身份鉴定的实现中,传统的方法是在服务端存储一个 当用户希望访问一个受保护的路由或者资源的时候,通常应该在
因为用户的状态在 JWT的这些特征使得我们可以完全依赖无状态的特性提供数据API服务。因为JWT并不使用Cookie的,所以你可以在任何域名提供你的API服务而不需要担心跨域资源共享问题(CORS) 下面的序列图展示了该过程: 中文流程介绍:
说了这么多JWT到底如何应用到我们的项目中,下面我们就使用SpringBoot 结合 JWT完成用户的登录验证。 应用流程
搭建SpringBoot JWT工程下面通过代码来实现用户认证的功能,博主这里主要采用Spring Boot与JWT整合的方式实现。关于Spring Boot项目如何搭建与使用本章不做详细介绍。
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
@Data@ConfigurationProperties(prefix = 'audience')@Componentpublic class Audience { private String clientId; private String base64Secret; private String name; private int expiresSecond;
} JWT验证主要是通过过滤器验证,所以我们需要添加一个拦截器来演请求头中是否包含有后台颁发的
package com.thtf.util;import com.thtf.common.exception.CustomException;import com.thtf.common.response.ResultCode;import com.thtf.model.Audience;import io.jsonwebtoken.*;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import javax.crypto.spec.SecretKeySpec;import javax.xml.bind.DatatypeConverter;import java.security.Key;import java.util.Date;/**
* ========================
* Created with IntelliJ IDEA.
* User:pyy
* Date:2019/7/17 17:24
* Version: v1.0
* ========================
*/public class JwtTokenUtil { private static Logger log = LoggerFactory.getLogger(JwtTokenUtil.class); public static final String AUTH_HEADER_KEY = 'Authorization'; public static final String TOKEN_PREFIX = 'Bearer '; /**
* 解析jwt
* @param jsonWebToken
* @param base64Security
* @return
*/
public static Claims parseJWT(String jsonWebToken, String base64Security) { try {
Claims claims = Jwts.parser()
.setSigningKey(DatatypeConverter.parseBase64Binary(base64Security))
.parseClaimsJws(jsonWebToken).getBody(); return claims;
} catch (ExpiredJwtException eje) {
log.error('===== Token过期 =====', eje); throw new CustomException(ResultCode.PERMISSION_TOKEN_EXPIRED);
} catch (Exception e){
log.error('===== token解析异常 =====', e); throw new CustomException(ResultCode.PERMISSION_TOKEN_INVALID);
}
} /**
* 构建jwt
* @param userId
* @param username
* @param role
* @param audience
* @return
*/
public static String createJWT(String userId, String username, String role, Audience audience) { try { // 使用HS256加密算法
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis); //生成签名密钥
byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(audience.getBase64Secret());
Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName()); //userId是重要信息,进行加密下
String encryId = Base64Util.encode(userId); //添加构成JWT的参数
JwtBuilder builder = Jwts.builder().setHeaderParam('typ', 'JWT') // 可以将基本不重要的对象信息放到claims
.claim('role', role)
.claim('userId', userId)
.setSubject(username) // 代表这个JWT的主体,即它的所有人
.setIssuer(audience.getClientId()) // 代表这个JWT的签发主体;
.setIssuedAt(new Date()) // 是一个时间戳,代表这个JWT的签发时间;
.setAudience(audience.getName()) // 代表这个JWT的接收对象;
.signWith(signatureAlgorithm, signingKey); //添加Token过期时间
int TTLMillis = audience.getExpiresSecond(); if (TTLMillis >= 0) { long expMillis = nowMillis TTLMillis;
Date exp = new Date(expMillis);
builder.setExpiration(exp) // 是一个时间戳,代表这个JWT的过期时间;
.setNotBefore(now); // 是一个时间戳,代表这个JWT生效的开始时间,意味着在这个时间之前验证JWT是会失败的
} //生成JWT
return builder.compact();
} catch (Exception e) {
log.error('签名失败', e); throw new CustomException(ResultCode.PERMISSION_SIGNATURE_ERROR);
}
} /**
* 从token中获取用户名
* @param token
* @param base64Security
* @return
*/
public static String getUsername(String token, String base64Security){ return parseJWT(token, base64Security).getSubject();
} /**
* 从token中获取用户ID
* @param token
* @param base64Security
* @return
*/
public static String getUserId(String token, String base64Security){
String userId = parseJWT(token, base64Security).get('userId', String.class); return Base64Util.decode(userId);
} /**
* 是否已过期
* @param token
* @param base64Security
* @return
*/
public static boolean isExpiration(String token, String base64Security) { return parseJWT(token, base64Security).getExpiration().before(new Date());
}
}
package com.thtf.config;import com.thtf.interceptor.JwtInterceptor;import org.springframework.boot.web.servlet.FilterRegistrationBean;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.CorsRegistry;import org.springframework.web.servlet.config.annotation.InterceptorRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/**
* ========================
* Created with IntelliJ IDEA.
* User:pyy
* Date:2019/7/18 10:37
* Version: v1.0
* ========================
*/@Configurationpublic class WebConfig implements WebMvcConfigurer { /**
* 添加拦截器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) { //拦截路径可自行配置多个 可用 ,分隔开
registry.addInterceptor(new JwtInterceptor()).addPathPatterns('/**');
} /**
* 跨域支持
*
* @param registry
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping('/**')
.allowedOrigins('*')
.allowCredentials(true)
.allowedMethods('GET', 'POST', 'DELETE', 'PUT', 'PATCH', 'OPTIONS', 'HEAD')
.maxAge(3600 * 24);
}
} 这里JWT可能会有跨域问题,配置跨域支持。
没有登录时候直接访问:http://localhost:8080/users 接口: 执行登录: 携带生成token再次访问:http://localhost:8080/users 接口
|
|