JWT实现单点登录(sso)功能

单点登录描述:
单点登录主要时应用在微服务架构中,在任意一个子服务中输入用户的用户名,密码进行登录时,
在跳转到其他系统的时候,就无需在进行登录,直接可以识别出用户的身份,权限以及角色等信息

.
.
JWT:全称java web token, 本质时一组加密后的字符串

JWT有三部分组成: header + payload+ sign (头+载荷+签名)
header记录: jwt使用的加密算法的类型,是一个json字符串,使用base64编码
payload载荷记录: 用户的用户名,用户角色,用户权限,jwt失效时间等循序,使用的时json字符串格式,使用了base64编码
sign签名: 包含了header+payload
JWT中三部分之间使用英文的句号隔开, 也就是 header头.paylouad载荷.sign签名
.
.
.
.
JWT实现单点登录的业务流程:
1,用户通过浏览器发送请求,访问网管,网管中有鉴权过滤器,校验之后,发现用户访问的是认证的微服务,则将请1求放行到认证微服务中
.
.
2,用户在认证微服务的登录页面中, 输入用户名, 密码, 然后登录. 登录后生成jwt字符
串, jwt字符串中有用的用户名, 用户的角色, 用户的权限信息, 返回保存到用户浏览器
cookie中

.
.
*3,用户访问其他服务时,携带token访问,每次访问校验token是否合法有效

JWT实现加密,解密:工具类

public class JwtUtil {

    //有效期为
    public static final Long JWT_TTL = 3600000L;// 60 * 60 *1000  一个小时
    //设置秘钥明文
    public static final String JWT_KEY = "qianfeng";

    /**
     * 创建token
     * @param id
     * @param subject
     * @param ttlMillis
     * @return
     */
    public static String createJWT(String id, String subject, Long ttlMillis) {

        /*获取加密类型*/
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

        /*记录签发时间*/
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        if(ttlMillis==null){
            ttlMillis=JwtUtil.JWT_TTL;
        }

        /*记录过期时间*/
        long expMillis = nowMillis + ttlMillis;
        Date expDate = new Date(expMillis);

        /*拿到秘钥*/
        SecretKey secretKey = generalKey();

        /*将上面获取的变量全部设置到指定参数位置进行加密,并通过builder对象中的compact方法返回加密的字符串*/
        JwtBuilder builder = Jwts.builder()
                .setId(id)              //唯一的ID
                .setSubject(subject)   // 主题  可以是JSON数据
                .setIssuer("admin")     // 签发者
                .setIssuedAt(now)      // 签发时间
                .signWith(signatureAlgorithm, secretKey) //使用HS256对称加密算法签名, 第二个参数为秘钥
                .setExpiration(expDate);// 设置过期时间
        return builder.compact();
    }

    /**
     * 解析
     *
     * @param jwt
     * @return
     * @throws Exception
     */
    public static Claims parseJWT(String jwt) throws Exception {
        /*获取秘钥
        * 通过Jwts的parser方法,设置秘钥,设置前端传进来的加密的字符串jwt,底层内部实现对比*/
        SecretKey secretKey = generalKey();
        return Jwts.parser()
                .setSigningKey(secretKey)
                .parseClaimsJws(jwt)
                .getBody();
    }

    /**
     * 生成加密后的秘钥 secretKey
     * @return
     */
    public static SecretKey generalKey() {
        /*对秘钥进行base64编码,防止乱码,生成字节数组
        * 用SecretKeySpec对象创建密文(通过有参构造方法:参数1:字节数组,参数2-3:字节的起始和终止位置,参数3:加密的算法名字)*/
        byte[] encodedKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return key;
    }
}

认证微服务模块的逻辑判断:


@Component
public class AuthFilter implements Ordered, GlobalFilter {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        /*获取响应对象*/
        ServerHttpRequest request = exchange.getRequest();
        /*从请求中获取用户的url*/
        ServerHttpResponse response = exchange.getResponse();

        /*解析路径*/
        String path = request.getURI().getPath();

        if(StringUtils.isEmpty(path)){
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return  response.setComplete();
        }

        /*判断url是否是登录路劲或者是注册路径,是就直接无条件放行*/
        if(path.contains("/auth/login")||path.contains("/auth/regist")){
            /*放行*/
            return chain.filter(exchange);
        }

        /*如果不是,则需要取jwt*/
        MultiValueMap<String, HttpCookie> cookies = request.getCookies();

        if(cookies==null){
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return  response.setComplete();
        }

        /*获取第一个名字叫做token的cookie值*/
        HttpCookie cookie = cookies.getFirst("token");

        if(cookie==null){
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return  response.setComplete();
        }

        /*判断是否能取取到jwt*/
        String  token = cookie.getValue();
        if(StringUtils.isEmpty(token)){
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return  response.setComplete();
        }

        /*如果能取出来,则将解析jwt,然后放行*/
        try {
            Claims claims = JwtUtil.parseJWT(token);
        } catch (Exception e) {
            e.printStackTrace();
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        }
        return   chain.filter(exchange);
    }

注意,这里的放行配合网关实现请求分发到指定微服务上:


server:
  port: 8082
spring:
  application:
    name: gateway
#注册中心
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.200.129:8848
#配置网关(3个微服务模块)
    gateway:
      routes:
        - id: customer
          uri: lb://customer
          predicates:
            - Path=/consumer/**

        - id: provider
          uri: lb://provider
          predicates:
            - Path=/provide/**

        - id: loginserver
          uri: lb://loginserver
          predicates:
            - Path=/auth/**


案例的微服务模块
JWT实现单点登录(sso)功能_第1张图片

你可能感兴趣的:(SpringCloud,java,开发语言,后端)