jwt单点登录

 一、前言

在我们日常开发中登录是每个系统都要做的,对于单点登录是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。

开一下传统登录:

jwt单点登录_第1张图片

单点登录:

jwt单点登录_第2张图片 

通俗讲:用户登录一次,就可已访问系统里的其他子系统。 

二、JWT是什么


1.JWT的概况


通俗讲:就是一种生存字符串的规则叫JWT。JWT是JSON WEB TOKEN的缩写,它是基于 RFC 7519 标准定义的一种可以安全传输的的JSON对象,由于使用了数字签名,所以是可信任和安全的。

 Token(令牌)属于无状态,因每个人制定的规则不同,生成的结果也不同。目前,多数人采用JWT为统一令牌标准,之后我也采用JWT作为token生成标准。

2.JWT的组成


jwt实例可以在该网站上获得解析结果:JSON Web Tokens - jwt.io

组成: 

jwt单点登录_第3张图片

jwt单点登录_第4张图片

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

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

JWT头:

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

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

 

在上面的代码中,

alg属性表示签名使用的算法,默认为HMAC SHA256(写为HS256);

typ属性表示令牌的类型,JWT令牌统一写为JWT。

最后,使用Base64 URL算法将上述JSON对象转换为字符串保存。

有效载荷【用户信息】:

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

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

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

{
    "userId": "1234567890",
    "name": "zhangsan",
    "admin": true
}

 

请注意,默认情况下JWT是未加密的,任何人都可以解读其内容,因此不要构建隐私信息字段,存放保密信息,以防止信息泄露。

JSON对象也使用Base64 URL算法转换为字符串保存。

签名哈希【防伪标志】:

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

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

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

Base64URL算法:
如前所述,JWT头和有效载荷序列化的算法都用到了Base64URL。该算法和常见Base64算法类似,稍有差别。 作为令牌的JWT可以放在URL中(例如api.example/?token=xxx)。 Base64中用的三个字符是"+","/“和”=",由于在URL中有特殊含义,因此Base64URL中对他们做了替换:"=“去掉,”+“用”-“替换,”/“用”_"替换,这就是Base64URL算法。

3.JWT的用法


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

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

4.JWT优缺点

  • JWT不仅可用于认证,还可用于信息交换。善用JWT有助于减少服务器请求数据库的次数。
  • 生产的token可以包含基本信息,比如id、用户昵称、头像等信息,避免再次查库
  • 存储在客户端,不占用服务端的内存资源
  • JWT默认不加密,但可以加密。生成原始令牌后,可以再次对其进行加密。
  • 当JWT未加密时,一些私密数据无法通过JWT传输。
  • JWT的最大缺点是服务器不保存会话状态,所以在使用期间不可能取消令牌或更改令牌的权限。也就是说,一旦JWT签发,在有效期内将会一直有效。
  • JWT本身包含认证信息,token是经过base64编码,所以可以解码,因此token加密前的对象不应该包含敏感信息,一旦信息泄露,任何人都可以获得令牌的所有权限。为了减少盗用,JWT的有效期不宜设置太长。对于某些重要操作,用户在使用时应该每次都进行进行身份验证。

为了减少盗用和窃取,JWT不建议使用HTTP协议来传输代码,而是使用加密的HTTPS协议进行传输。

三、Token实现单点登录(代码)

1.添加JWT依赖与JWT工具类

依赖:

       
        
            io.jsonwebtoken
            jjwt
            0.9.1
        

jwt工具类:

public class JwtUtils {
 
    //token过期时间
    public static final long EXPIRE = 1000 * 60 * 60 * 24;
 
    //秘钥,每个公司生成规则不一样
    public static final String APP_SECRET = "rikka六花";
 
    //生成token字符串方法,参数根据自己实际要求指定
    public static String getJwtToken(String id, String nickname) {
        String JwtToken = Jwts.builder()
                //设置jwt头信息,红色部分,内容固定,不需要改
                .setHeaderParam("typ", "JWT")
                .setHeaderParam("alg", "HS256")
 
                .setSubject("guli-user")//设置主题(可选)
                .setIssuedAt(new Date())//设置发布时间(可选)
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRE))//设置过期时间
 
                //设置token主体部分,存储用户信息,可设置多个值
                .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 {
            //根据设置的防伪码解析token
            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;
            //根据设置的防伪码解析token
            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 "";
        //根据设置的防伪码解析token,获取对象
        Jws claimsJws =
                Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
        //获取token有效载荷【用户信息】
        Claims claims = claimsJws.getBody();
        return (String) claims.get("id");
    }
 
 
}

2.编写登录方法

    @Override
    public String login(LoginVo loginVo) {
        //写你自己的具体登录逻辑:
        //1.判断参数是否为空
        ...
        //2.判断数据库是否存在该用户
        ...
        //3.判断加密后的密码参数和数据库是否一致
        ...
        //4.判断用户是否禁用
        ...
        //5.生成jwt令牌返回给前端
        return JwtUtils.getJwtToken(ucenterMember.getId(), ucenterMember.getNickname());
    }

 将JWT返回给前端,前端选择将令牌存入cookie或放进地址栏。之后每次都带着JWT发送请求,因为JWT有效载荷里存有id,所以下次后端可根据id查找对应用户信息返回。可根据是否有token判断是否登录,所以单点登录就实现了。 

你可能感兴趣的:(前端)