jwt生成token
SSO服务
一、流程
1、用户登录统一认证系统(uums)
2、uums判断登录是否有效
有效:生成token,并带生成的token返回给用户
无效:返回登录页面重新登录
3、用户登录uums子系统,uums对请求进行拦截,判断token是否有效
有效:正常访问
无效:返回重新登录 401(未授权)
二、token生成
JWT(JSON WEB TOKEN) 加密字符串:BASE64+HS256算法
第一步:头部信息 BASE64 (A)
第二步:载荷信息 BASE64 (B)
第三步:(A+B) HS256算法 (C)
第四部: JWT = A+B+C
三、token验证
1、客户端发出请求(get、post、api、页面)
2、uums对请求进行拦截
3、判断需要验证token
4、查找token
a.cookie中查找是否存在token
b.HTTP Authorization Head中查找是否存在token
5、验证token
a.通过配置文件取秘钥,对JWT进行解密解码
b.验证签名、载荷信息中的exp等信息
6、取用户权限、角色信息,进行角色判断
四、token总结
1、token是个信息集合
2、token中信息要足够的,以便减少对数据库的访问
3、token对cookie和HTTP Authorization Head进行检查
4、token签名需可解码 及 信息的有效性
package com.tonbusoft.uums.commons.utils; import io.jsonwebtoken.Claims; import io.jsonwebtoken.JwtBuilder; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import java.security.Key; import java.util.Date; import java.util.HashMap; import java.util.Map; import javax.crypto.spec.SecretKeySpec; import javax.xml.bind.DatatypeConverter; import com.tonbusoft.uums.commons.ConfigBean; import com.tonbusoft.uums.module.ua.bean.UaUser; /** * token工具类 JWT */ public class JWTUtils { /** * 生成token * @param id tokenId * @param issuer 签发者 UUMS * @param subject 面向用户 tongyi * @param nowMillis token生成时间 long * @param ttlMillis token生成后多久过期 long * @param user 生成token的用户信息 * @param ip 请求登录IP地址 * @return */ public static String createToken(String id, String issuer, String subject, long nowMillis, long ttlMillis, UaUser user, String ip) { // JWT签名算法 用HS256加密 SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; // 当前时间 // long nowMillis = System.currentTimeMillis(); Date now = new Date(nowMillis); // 进行加密用的秘钥 byte[] apiKeySecretBytes = DatatypeConverter .parseBase64Binary(ConfigBean.tokenSecret); Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName()); // 设置JWT Claims // 用签名算法HS256和私钥key生成token JwtBuilder builder = Jwts.builder().setId(id)// 版本号 .setIssuedAt(now)// 何时签发 时间戳 设置现在时间 // 它可以用来做一些maxAge之类的验证,假如验证时间与这个claim指定的时间相差的时间大于通过maxAge指定的一个值,就属于验证失败 .setSubject(subject)// 面向用户 抽象主题 .setIssuer(issuer)// 签发者 // .setAudience("")//设置角色 ['b.com','c.com']验证的时候这个claim的值至少要包含b.com,c.com的其中一个才能验证通过; .claim("user", user) .claim("ip", ip) .signWith(signatureAlgorithm, signingKey); // 加密方法 // 设置失效时间 // Date exp = null; // if (ttlMillis >= 0) { // long expMillis = nowMillis + ttlMillis; // exp = new Date(expMillis); // builder.setExpiration(exp);// 过期时间 // } // 设置序列化 URL安全化 String tokenString = builder.compact(); return tokenString; } /** * 刷新获取token * @param oldToken 之前旧的token * @return */ public static String refreshToken(String oldToken) { String newToken = ""; // 解密旧token // 验证签名 Claims claims = Jwts.parser() // 返回配置实例化后的实例 .setSigningKey(DatatypeConverter.parseBase64Binary(ConfigBean.tokenSecret)) // 根据配置文件中的秘钥进行解密 .parseClaimsJws(oldToken).getBody(); // 获取JWT中的载荷 // 获取相关信息 // 面向用户 String subject = claims.getSubject(); // 签发者 String issuer = claims.getIssuer(); // IP地址 String ip = claims.get("ip").toString(); // 用户信息 UaUser user = claims.get("user", UaUser.class); long nowMillis = System.currentTimeMillis(); long ttlMillis = 3600000; // 生成新token newToken = createToken("1", issuer, subject, nowMillis, ttlMillis, user, ip); return newToken; } //解密token public static Claims parseJWT(String token){ return Jwts.parser() //返回配置实例化后的实例 .setSigningKey(DatatypeConverter.parseBase64Binary(ConfigBean.tokenSecret)) //根据配置文件中的秘钥进行解密 .parseClaimsJws(token) .getBody(); } /** * 验证token * @param claims token解密后的信息集合 * @return */ public static Map<String, Object> validateToken (Claims claims, String Ip) { Map<String, Object> result = new HashMap<String, Object>(); String jwtId = claims.getId(); //面向用户 String subject = claims.getSubject(); //签发者 String issuer = claims.getIssuer(); //何时签发 Date issuedAt = claims.getIssuedAt(); //IP Object ip = claims.get("ip"); // UaUser user = claims.get("user", UaUser.class); //验证一 jwtId是否为1 if (!"1".equals(jwtId)) { System.out.println("token序列不为1 token验证不通过"); result.put("code", 1); result.put("flag", false); result.put("msg", "token 无效"); } else if (!"UUMS".equals(issuer)) { //验证二 签发者是否是UUMS System.out.println("签发者不是UUMS token验证不通过"); result.put("code", 2); result.put("flag", false); result.put("msg", "token 无效"); } else if (null == subject) { //验证三 面向用户是否是当前用户 System.out.println("当前用户为空 token验证不通过"); result.put("code", 3); result.put("flag", false); result.put("msg", "token 无效"); } else if (null ==ip) { //验证四 当前用户IP System.out.println("当前用户IP异常 token验证不通过"); result.put("code", 4); result.put("flag", false); result.put("msg", "token 无效"); } else if (!Ip.equals(ip.toString())) { //验证四 当前用户IP System.out.println("当前用户IP异常 token验证不通过"); result.put("code", 4); result.put("flag", false); result.put("msg", "token 无效"); } if (null != claims.get("user")) { //用户信息 String user = claims.get("user").toString(); String temp = user.substring(user.indexOf("xl=")); String[] s = temp.substring(0, temp.indexOf(",")).split("="); String zhxl = s[1]; HashMap<String,Object> tokenInfo = ApplicationCache.getTokeninfo(); // Integer zhxl = user.getXl(); //通过账户序列获取账户的过期时间 Long gqsj =(Long)tokenInfo.get(zhxl); //获取当前时间 Long nowMillis = System.currentTimeMillis(); if (gqsj <= nowMillis) { gqsj += 3600000; tokenInfo.put(zhxl.toString(), gqsj); ApplicationCache.setTokenInfo(tokenInfo); } result.put("code", 0); result.put("flag", true); result.put("msg", "token 验证通过"); } else { result.put("code", 4); result.put("flag", false); result.put("msg", "token 无效"); } return result; } //验证token private boolean validateToken(HttpServletRequest request, HttpServletResponse response) { //获取token Map<String, Object> result = null; boolean tokenFlag = false; String token = null; String authHeader = request.getHeader("Authorization"); if (null == authHeader || !authHeader.startsWith("Bearer ")) { } else { //截取掉Bearer 字符串 token = authHeader.substring(7); } if (null != token) { //验证token result = JWTUtils.validateToken(JWTUtils.parseJWT(token), IpUtil.getOuterIp(request)); tokenFlag = (boolean)result.get("flag"); } //token验证成功 如果过期但仍需操作 则默认刷新token 不必重写登录 if (tokenFlag) { response.setHeader("Authorization", "Bearer " + token); //token验证成功 return true; } else { //token验证失败 return false; } } }