SSO服務
1、流程java
一、用戶登陸統一認證系統(uums)web
二、uums判斷登陸是否有效算法
有效:生成token,並帶生成的token返回給用戶數據庫
無效:返回登陸頁面從新登陸json
三、用戶登陸uums子系統,uums對請求進行攔截,判斷token是否有效api
有效:正常訪問安全
無效:返回從新登陸 401(未受權)cookie
2、token生成工具
JWT(JSON WEB TOKEN) 加密字符串:BASE64+HS256算法post
第一步:頭部信息 BASE64 (A)
第二步:載荷信息 BASE64 (B)
第三步:(A+B) HS256算法 (C)
第四部: JWT = A+B+C
3、token驗證
一、客戶端發出請求(get、post、api、頁面)
二、uums對請求進行攔截
三、判斷須要驗證token
四、查找token
a.cookie中查找是否存在token
b.HTTP Authorization Head中查找是否存在token
五、驗證token
a.經過配置文件取祕鑰,對JWT進行解密解碼
b.驗證簽名、載荷信息中的exp等信息
六、取用戶權限、角色信息,進行角色判斷
4、token總結
一、token是個信息集合
二、token中信息要足夠的,以便減小對數據庫的訪問
三、token對cookie和HTTP Authorization Head進行檢查
四、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; } } }