跨域认证之JWT

JSON Web Token(缩写 JWT)目前比较流行的跨域认证解决方案

基于token的鉴权机制

1>    用户使用用户名密码来请求服务器

2>    服务器进行验证用户的信息

3>    服务器通过验证发送给用户一个token

4>    客户端存储token,并在每次请求时附送上这个token值

5>    服务端验证token值,并返回数据

JWT 的原理

服务器认证以后,生成一个 JSON 对象,发回给用户,

用户与服务端通信的时候,都要发回这个 JSON 对象。

服务器完全只靠这个对象认定用户身份。为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名。

服务器就不保存任何 session 数据, 容易实现扩展。

JWT 的组成

1>    Header(头部)

2>    Payload(负载)

3>    Signature(签名)

JWT是由三段信息构成的,将这三段信息文本用.链接一起就构成了Jwt字符串

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

header  

header头部是一个json对象,包含两部分信息:

{

  'typ': 'JWT',

  'alg': 'HS256'

}

alg属性表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256);typ属性表示这个令牌(token)的类型(type),JWT 令牌统一写为JWT。

最后,将上面的 JSON 对象使用 Base64URL 算法转成字符串

playload

Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段,供选用。

1>    iss (issuer):签发人

2>    exp (expiration time):过期时间

3>    sub (subject):主题

4>    aud (audience):受众

5>    nbf (Not Before):生效时间

6>    iat (Issued At):签发时间

7>    jti (JWT ID):编号

除了官方字段,还可以在这个部分定义私有字段,比如:

{

  "sub": "1234567890",

  "name": "zhangsan",

  "admin": true

}

注意,JWT 默认是不加密的,任何人都可以读到,所以不要把秘密信息放在这个部分。

这个 JSON 对象也要使用 Base64URL 算法转成字符串。

Signature

Signature 部分是对前两部分的签名,防止数据篡改。

首先需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。

HMACSHA256(

  base64UrlEncode(header) + "." +

  base64UrlEncode(payload),

  secret)

算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户。

Base64URL

前面Header 和 Payload 串型化的算法是 Base64URL。这个算法跟 Base64 算法基本类似,但有一些小的不同。

JWT 作为一个令牌(token),有些场合可能会放到 URL(比如 api.example.com/?token=xxx)。Base64 有三个字符+、/和=,在 URL 里面有特殊含义,所以要被替换掉:=被省略、+替换成-,/替换成_ 。这就是 Base64URL 算法。

特点说明

JWT 默认是不加密,生成 Token 以后,可以用密钥再加密一次。

JWT 不加密的情况下,不能将秘密数据写入 JWT。

JWT 可以用于认证,也可以用于少量的数据传递。降低服务器查询数据库的次数。

由于服务器不保存 session 状态,因此无法在使用过程中废止某个 token,或者更改 token 的权限。也就是说,一旦 JWT 签发了,在到期之前就会始终有效,

JWT 包含了认证信息,为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时可再次对用户进行认证。

为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。

JWT使用

引入依赖

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>${jjwt.version}</version>
</dependency>

  

用户登录

@RestController
@RequestMapping("/user")
public class UserController{

    @Autowired
    private IUserService userService;

    @PostMapping(value = "/login")
    public Map<String,Object> login(User user, HttpServletResponse reponse) {
            Map<String,Object> result=new HashMap<>();
            User user = userService.login(user);
            String token = TokenUtil.generateToken(user);
            reponse.setHeader("token", token);
            result.put("user",user);
            return result;
    }

}

  

生成token

//JWT密钥
private static String secret = "xxxjwtsecret";


/**
 * 根据用户信息生成token
 */
public static String generateToken(TokenUser user) {
    Map<String, Object> claims = new HashMap<>();
    claims.put("id", user.getId());
    claims.put("deptId", user.getDeptId());
    claims.put("userName", user.getUserName());
    claims.put("isAdmin", user.getIsAdmin());

    return Jwts.builder()
            .setClaims(claims)
            .setExpiration(generateExpirationDate())
            .signWith(SignatureAlgorithm.HS512, secret)
            .compact();
}

  

token认证校验

String token = request.getHeader("token");// 取出token
// token 校验
if (StringUtils.isEmpty(token)) {
    return false;
}
// 校验token是否已过期
Claims claims = Jwts.parser()
        .setSigningKey(secret)
        .parseClaimsJws(token)
        .getBody();
if (claims.getExpiration().before(new Date())) {
    return false;
}

  

根据token和密钥取出用户信息

Claims claims = Jwts.parser()
        .setSigningKey(secret)
        .parseClaimsJws(token)
        .getBody();

    User user = new User();
    user.setId(claims.get("id", Long.class));
    user.setDeptId(claims.get("deptId", Long.class));
    user.setIsAdmin(claims.get("isAdmin", Boolean.class));
    user.setUserName(claims.get("userName", String.class));

  

原文地址:https://www.cnblogs.com/brant/p/12641981.html