定义:JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案算法
JWT官网数据库
因为HTTP协议是无状态的,这意味着若是咱们想断定一个接口是否被认证后访问,就须要借助cookie或者session会话机制进行断定,可是因为如今的系统架构大部分都不止一台服务器,此时又要借助数据库或者全局缓存 作存储,这种方案显然受限太多。api
那么咱们可不可让认证令牌的发布者本身去识别这个令牌是否是我曾经发布的令牌呢(JWT核心思想),这是JWT最大的优势也是最大的缺点,优势是简单快捷、不须要依赖任何第三方操做就能实现身份认证,缺点就是对于任何拥有用户发布令牌的请求都会认证经过。跨域
正常的JWT数据结构应该以下缓存
它是一个很长的字符串,中间用点(.
)分隔成三个部分服务器
JWT的三个部分依次: Header - 头部 、Payload - 负载 、Signature(签名)cookie
即:Header.Payload.Signaturesession
Header 部分是一个 JSON 对象,描述 JWT 的元数据,一般是下面的样子。数据结构
{
"alg": "HS256",
"typ": "JWT"
}
alg
属性表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256);typ
属性表示这个令牌(token)的类型(type),JWT 令牌统一写为JWT
Payload 部分也是一个 JSON 对象,用来存放实际须要传递的数据。JWT 规定了7个官方字段,供选用。
iss (issuer):签发人
exp (expiration time):过时时间
sub (subject):主题
aud (audience):受众
nbf (Not Before):生效时间
iat (Issued At):签发时间
jti (JWT ID):编号
除了官方字段,你还能够在这个部分定义私有字段
{
"sub": "1234567890",
"name": "John Doe",
"age": "19"
}
注意:JWT默认是明文展现,任何人均可以读取到,因此此处不要放私密信息
这个 JSON 对象也要使用 Base64URL 算法转成字符串。
Signature 部分是对前两部分的签名,防止数据篡改。
首先,须要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。而后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
算出签名之后,把 Header、Payload、Signature 三个部分拼成一个字符串,每一个部分之间用"点"(.
)分隔,就能够返回给用户。
前面提到,Header 和 Payload 串型化的算法是 Base64URL。这个算法跟 Base64 算法基本相似,但有一些小的不一样。
JWT 做为一个令牌(token),有些场合可能会放到 URL(好比 api.example.com/?token=xxx)。Base64 有三个字符+
、/
和=
,在 URL 里面有特殊含义,因此要被替换掉:=
被省略、+
替换成-
,/
替换成_
。这就是 Base64URL 算法
Maven依赖
<dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.5.0</version> </dependency>
JWT签名发布和验证代码
public class TokenUtil { //Token的过时时间 private static final long EXPIRE_TIME = 30 * 60 * 1000; //Token的私钥 private static final String TOKEN_SECRET = "jytoken_secret"; /** * 生成签名,30分钟过时 * @param **userInfo** 用户信息 用户姓名 * @param **other** 用户其余信息 用户id * @return */ public static String sign(String userInfo, String other) { try { // 设置过时时间 Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME); //私钥和加密算法 Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET); // 设置头部信息 Map<String, Object> header = new HashMap<>(2); header.put("Type", "Jwt"); header.put("alg", "HS256"); // 返回token字符串 return JWT.create() .withHeader(header) .withClaim("userInfo", userInfo) .withClaim("other", other) .withExpiresAt(date) .sign(algorithm); } catch (Exception e) { e.printStackTrace(); return null; } } /** * 生成签名,30分钟过时 * @param **userInfo** 用户信息 用户姓名 * @param **other** 用户其余信息 用户id * @return */ public static String sign(String userInfo, String other,long expire) { try { // 设置过时时间 Date date = new Date(System.currentTimeMillis() + expire); //私钥和加密算法 Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET); // 设置头部信息 Map<String, Object> header = new HashMap<>(2); header.put("Type", "Jwt"); header.put("alg", "HS256"); // 返回token字符串 return JWT.create() .withHeader(header) .withClaim("userInfo", userInfo) .withClaim("other", other) .withExpiresAt(date) .sign(algorithm); } catch (Exception e) { e.printStackTrace(); return null; } } /** * 检验token是否正确 * @param **token** * @return */ public static boolean verify(String token){ try { Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET); JWTVerifier verifier = JWT.require(algorithm).build(); verifier.verify(token);//未验证经过会抛出异常 return true; } catch (Exception e){ return false; } } /** * 从token中获取info信息 * @param **token** * @return */ public static String getUserName(String token,String info){ try { DecodedJWT jwt = JWT.decode(token); return jwt.getClaim(info).asString(); } catch (JWTDecodeException e){ e.printStackTrace(); } return null; } }
拦截器配置无需认证的请求
@Configuration public class InterceptorConfig extends WebMvcConfigurationSupport { @Autowired private TokenHandler tokenHandler; @Override public void addInterceptors(InterceptorRegistry registry) { List<String> excludePath = new ArrayList<>(); String checkLogin = "/pushlogin/checkIsCanLogin"; String login = "/pushlogin/login"; String getVerifyCode = "/common/send"; String verfifyMethod = "/common/validationCode"; excludePath.add(checkLogin); excludePath.add(login); excludePath.add(getVerifyCode); excludePath.add(verfifyMethod); registry.addInterceptor(tokenHandler).excludePathPatterns(excludePath); } }
Token统一拦截器代码
@Component @Slf4j public class TokenHandler implements HandlerInterceptor{ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token = request.getHeader("Authentication"); if (token != null){ boolean result = TokenUtil.verify(token); if(result){ log.info("经过拦截器"); return true; } } log.info("认证失败"); return false; } }
用户登陆时验证用户信息后,返回Token信息
@Override public UserDTO selectIsExistUserInfo(String phone) { //TODO 伪代码 验证用户信息 UserDTO info = 查询用户信息 if (info != null) { String token = TokenUtil.sign(info.getUsername(), info.getUserId(), 6 * 60 * 60 * 1000); info.setToken(token); } return info; }