单点登录-基于JWT机制的单点登录

1、认证过程

登录:

单点登录-基于JWT机制的单点登录_第1张图片

1)第一次认证:第一次登录,用户从浏览器输入用户名/密码,提交后到服务器的登录处理的Action层(Login Action);

2)Login Action调用认证服务进行用户名密码认证,如果认证通过,Login Action层调用用户信息服务获取用户信息(包括完整的用户信息及对应权限信息);

3)返回用户信息后,Login Action从配置文件中获取Token签名生成的秘钥信息,进行Token的生成;

4)生成Token的过程中可以调用第三方的JWT Lib生成签名后的JWT数据;

5)完成JWT数据签名后,将其设置到COOKIE对象中,并重定向到首页,完成登录过程;

请求认证:

基于Token的认证机制会在每一次请求中都带上完成签名的Token信息,这个Token信息可能在COOKIE
中,也可能在HTTP的Authorization头中;

单点登录-基于JWT机制的单点登录_第2张图片

1)客户端(APP客户端或浏览器)通过GET或POST请求访问资源(页面或调用API);

2)认证服务作为一个Middleware HOOK 对请求进行拦截,首先在cookie中查找Token信息,如果没有找到,则在HTTP Authorization Head中查找;

3)如果找到Token信息,则根据配置文件中的签名加密秘钥,调用JWT Lib对Token信息进行解密和解码;

4)完成解码并验证签名通过后,对Token中的exp、nbf、aud等信息进行验证;

5)全部通过后,根据获取的用户的角色权限信息,进行对请求的资源的权限逻辑判断;

6)如果权限逻辑判断通过则通过Response对象返回;否则则返回HTTP 401;

2、部分代码

用户登录时:

单点登录-基于JWT机制的单点登录_第3张图片

用户请求时:

单点登录-基于JWT机制的单点登录_第4张图片

JWTUtils.java:

package com.sxt.sso.commons;

import java.util.Date;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.SignatureException;

/**
 * JWT工具
 */
public class JWTUtils {
   
   // 服务器的key。用于做加解密的key数据。 如果可以使用客户端生成的key。当前定义的常亮可以不使用。
   private static final String JWT_SECERT = "test_jwt_secert" ;
   private static final ObjectMapper MAPPER = new ObjectMapper();
   public static final int JWT_ERRCODE_EXPIRE = 1005;//Token过期
   public static final int JWT_ERRCODE_FAIL = 1006;//验证不通过

   public static SecretKey generalKey() {
      try {
         // byte[] encodedKey = Base64.decode(JWT_SECERT); 
         // 不管哪种方式最终得到一个byte[]类型的key就行
         byte[] encodedKey = JWT_SECERT.getBytes("UTF-8");
          SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
          return key;
      } catch (Exception e) {
         e.printStackTrace();
          return null;
      }
   }
   /**
    * 签发JWT,创建token的方法。
    * @param id  jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
    * @param iss jwt签发者
    * @param subject jwt所面向的用户。payload中记录的public claims。当前环境中就是用户的登录名。
    * @param ttlMillis 有效期,单位毫秒
    * @return token, token是一次性的。是为一个用户的有效登录周期准备的一个token。用户退出或超时,token失效。
    * @throws Exception
    */
   public static String createJWT(String id,String iss, String subject, long ttlMillis) {
      // 加密算法
      SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
      // 当前时间。
      long nowMillis = System.currentTimeMillis();
      // 当前时间的日期对象。
      Date now = new Date(nowMillis);
      SecretKey secretKey = generalKey();
      // 创建JWT的构建器。 就是使用指定的信息和加密算法,生成Token的工具。
      JwtBuilder builder = Jwts.builder()
            .setId(id)  // 设置身份标志。就是一个客户端的唯一标记。 如:可以使用用户的主键,客户端的IP,服务器生成的随机数据。
            .setIssuer(iss)
            .setSubject(subject)
            .setIssuedAt(now) // token生成的时间。
            .signWith(signatureAlgorithm, secretKey); // 设定密匙和算法
      if (ttlMillis >= 0) { 
         long expMillis = nowMillis + ttlMillis;
         Date expDate = new Date(expMillis); // token的失效时间。
         builder.setExpiration(expDate);
      }
      return builder.compact(); // 生成token
   }
   
   /**
    * 验证JWT
    * @param jwtStr
    * @return
    */
   public static JWTResult validateJWT(String jwtStr) {
      JWTResult checkResult = new JWTResult();
      Claims claims = null;
      try {
         claims = parseJWT(jwtStr);
         checkResult.setSuccess(true);
         checkResult.setClaims(claims);
      } catch (ExpiredJwtException e) { // token超时
         checkResult.setErrCode(JWT_ERRCODE_EXPIRE);
         checkResult.setSuccess(false);
      } catch (SignatureException e) { // 校验失败
         checkResult.setErrCode(JWT_ERRCODE_FAIL);
         checkResult.setSuccess(false);
      } catch (Exception e) {
         checkResult.setErrCode(JWT_ERRCODE_FAIL);
         checkResult.setSuccess(false);
      }
      return checkResult;
   }
   
   /**
    * 
    * 解析JWT字符串
    * @param jwt 就是服务器为客户端生成的签名数据,就是token。
    * @return
    * @throws Exception
    */
   public static Claims parseJWT(String jwt) throws Exception {
      SecretKey secretKey = generalKey();
      return Jwts.parser()
         .setSigningKey(secretKey)
         .parseClaimsJws(jwt)
         .getBody(); // getBody获取的就是token中记录的payload数据。就是payload中保存的所有的claims。
   }
   
   /**
    * 生成subject信息
    * @param subObj - 要转换的对象。
    * @return java对象->JSON字符串出错时返回null
    */
   public static String generalSubject(Object subObj){
      try {
         return MAPPER.writeValueAsString(subObj);
      } catch (JsonProcessingException e) {
         e.printStackTrace();
         return null;
      }
   }
   
}

 

你可能感兴趣的:(单点登录及认证)