SSO单点登录和JWT

1、用户登录业务介绍

1.1 单一服务器模式

早期单一服务器,使用session对象来实现

  • 登录成功之后,把用户数据放到session里面。
  • 从session获取数据判断是否登录

缺点:单点性能压力,无法扩展。而且在实际项目一般都是多台服务器做集群操作。

1.2 SSO(single sign on)模式--单点登录

比如我们这个项目采用的是微服务架构,也就是说每个功能模块都是独立运行的,每个功能模块的服务器都是不同的。

那么采用1.1的session方式登录的话,我们在访问admin_lab要登录一次,访问admin_oss要登录一次,因为每个模块对应不同的服务器,所以每个模块的session也就不是共享的。

而我们要做的效果应该是,只要在一个模块登录之后,就不需要再去其他模块登录(这里好比我们访问百度的时候,我们登录百度账号,那么我们访问通过网页访问百度贴吧的时候也就不要再重新登录)。

实现这种效果的一种技术叫做单点登录:用户只需要登录一次就可以访问所有相互信任的应用系统。

2、单点登录三种常见方式:

2.1 session广播机制实现

实则就是session复制,比如你在admin_lab模块登录之后,该模块的session对象会复制到其他模块。

缺点:如果模块特别多的话,session需要复制很多次,会造成数据重复,消耗很多资源,所以不推荐这种做法。

2.2 cookie+redis实现

首先说下cookie和redis:

cookie是存放在客户端的,浏览器每次发送请求都会带着cookie值进行发送。

redis数据以”key/value“形式存储。

如何实现?

1、在项目任何一个模块中进行登录,登录之后把数据放在两个地方

  1. redis:key值:生成唯一随机值(ip,用户id等等,可以通过UUID等技术实现),value值:存放用户数据
  2. cookie:把redis里面生成的key值放到cookie里面

2、访问项目中其他模块,因为发送请求时会携带cookie,那么我们获取cookie值(里面存放着redis生成的key值),通过cookie值到redis里面进行查询,根据key进行查询,如果查询到用户数据就表明已经登录

2.3 使用token实现

首先说下token是什么?

按照一定规则生成的字符串,字符串可以包含用户信息。

如何实现?

1、在项目某个模块进行登录,登录之后,按照规则生成特定的字符串(把登录之后用户包含到生成字符串里面),然后再返回字符串。有两种方式返回字符串:

  1. 可以把字符串通过cookie放回
  2. 把字符串通过地址栏返回

2、再去访问其他模块的时候,每次访问都在地址栏带着生成的字符串,在访问模块里面获取地址栏字符串,根据字符串获取用户信息,如果获取到用户信息就表明用户已经登录。

这里提一下,session的默认过期时间是30分钟!!2.2和2.3虽然没有session但是也可以通过redis过期时间和token过期时间模拟实现。

三、JWT

3.1 简介

JWT对象为一个很长的字符串,字符之间通过"."分隔符分为三个子串。

每一个子串表示了一个功能块,总共有以下三个部分:JWT头、有效载荷和签名

3.2 JWT的三大部分

3.2.1 JWT头部信息

JWT头部分是一个描述JWT元数据的JSON对象,通常如下所示。

{
  "alg": "HS256",
  "typ": "JWT"
}

在上面的代码中,alg属性表示签名使用的算法,默认为HMAC SHA256(写为HS256);typ属性表示令牌的类型,JWT令牌统一写为JWT。最后,使用Base64 URL算法将上述JSON对象转换为字符串保存。

3.2.2 有效载荷--主体内容

有效载荷部分,是JWT的主体内容部分,也是一个JSON对象,包含需要传递的数据(比如用户名)。 JWT指定七个默认字段供选择

iss:发行人
exp:到期时间
sub:主题
aud:用户
nbf:在此之前不可用
iat:发布时间
jti:JWT ID用于标识该JWT

除以上默认字段外,我们还可以自定义私有字段,如下例:

{
  "sub": "1234567890",
  "name": "Helen",
  "admin": true
}

注意,默认情况下JWT是未加密的,任何人都可以解读其内容,因此不要构建隐私信息字段,存放保密信息,以防止信息泄露。JSON对象也使用Base64 URL算法转换为字符串保存。

3.2.3 签名哈希

签名哈希部分是对上面两部分数据签名,通过指定的算法生成哈希,以确保数据不会被篡改。

首先,需要指定一个密码(secret)。该密码仅仅为保存在服务器中,并且不能向用户公开。然后,使用标头中指定的签名算法(默认情况下为HMAC SHA256)根据以下公式生成签名。

HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(claims), secret)

 在计算出签名哈希后,JWT头,有效载荷和签名哈希的三个部分组合成一个字符串,每个部分用"."分隔,就构成整个JWT对象。

3.3 JWT原则

JWT的原则是在服务器身份验证之后,将生成一个JSON对象并将其发送回用户,如下所示。

{
  "sub": "1234567890",
  "name": "Helen",
  "admin": true
}

之后,当用户与服务器通信时,客户在请求中发回JSON对象。服务器仅依赖于这个JSON对象来标识用户。为了防止用户篡改数据,服务器将在生成对象时添加签名。

服务器不保存任何会话数据,即服务器变为无状态,使其更容易扩展。

3.4 JWT用法

客户端接收服务器返回的JWT,将其存储在Cookie或localStorage中。

此后,客户端将在与服务器交互中都会带JWT。如果将它存储在Cookie中,就可以自动发送,但是不会跨域,因此一般是将它放入HTTP请求的Header Authorization字段中。当跨域时,也可以将JWT被放置于POST请求的数据主体中。

3.5 JWT 优点和缺点

3.5.1 优点

  • JWT不仅可用于认证,还可用于信息交换。善用JWT有助于减少服务器请求数据库的次数。
  • 生产的token可以包含基本信息,比如id、用户昵称、头像等信息,避免再次查库
  • 存储在客户端,不占用服务端的内存资源
  • JWT默认不加密,但可以加密。生成原始令牌后,可以再次对其进行加密。

3.5.2 缺点

  • 当JWT未加密时,一些私密数据无法通过JWT传输。
  • JWT的最大缺点是服务器不保存会话状态,所以在使用期间不可能取消令牌或更改令牌的权限。也就是说,一旦JWT签发,在有效期内将会一直有效。
  • JWT本身包含认证信息,token是经过base64编码,所以可以解码,因此token加密前的对象不应该包含敏感信息,一旦信息泄露,任何人都可以获得令牌的所有权限。为了减少盗用,JWT的有效期不宜设置太长。对于某些重要操作,用户在使用时应该每次都进行进行身份验证。
  • 为了减少盗用和窃取,JWT不建议使用HTTP协议来传输代码,而是使用加密的HTTPS协议进行传输。

3.6 整合JWT令牌

package com.zyk.commonUtils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;

/**
 * JWT工具类
 * @author
 * @date 2021/5/2 0002 - 下午 12:52
 */
public class JwtUtils {

    public static final long EXPIRE = 1000 * 60 * 60 * 24; //token过期时间
    public static final String APP_SECRET="ukc8BDbRigUDaY6pZFfWus2jZWLPHO";  //秘钥

    public static String getJwtToken(String id, String nickname){

        String JwtToken = Jwts.builder()
                //设置JWT头部信息
                .setHeaderParam("typ", "JWT")
                .setHeaderParam("alg", "HS256")
                //设置过期时间
                .setSubject("lab-yk")
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis()+EXPIRE))
                //设置JWT主体内容--存储用户信息
                .claim("id", id)
                .claim("nickname", nickname)
                //设置哈希签名
                .signWith(SignatureAlgorithm.HS256,APP_SECRET)
                .compact();
        return JwtToken;
    }

    /**
     * 判断token是否存在有效
     * @param jwtToken
     * @return
     */
    public static boolean checkToken(String jwtToken){
        if(StringUtils.isEmpty(jwtToken)) return false;
        try{
            Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 判断token是否存在与有效
     * @param request
     * @return
     */
    public static boolean checkToken(HttpServletRequest request) {
        try {
            String jwtToken = request.getHeader("token");
            if(StringUtils.isEmpty(jwtToken)) return false;
            Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 根据token获取会员id
     * @param request
     * @return
     */
    public static String getMemberIdByJwtToken(HttpServletRequest request) {
        String jwtToken = request.getHeader("token");
        if(StringUtils.isEmpty(jwtToken)) return "";
        Jws<Claims> claimsJws = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
        Claims claims = claimsJws.getBody();
        return (String)claims.get("id");
    }
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值