03 使用jwt完成登录验证

1、理论阐述

前两节我们使用redis+cookie完成了单点登录,但这种方式有缺陷,那就是用户数据登录信息需要存储到redis,如果redis固化失败,则整个单点登录方案失效。同时,这一种方案比较重,数据集中化存储在服务端,能不能轻装上阵,数据不存储于服务端,倘若服务器不保存任何登录数据,即服务器变为无状态,则其更容易扩展。我们马上要看的JSON Web Token(JWT)就是将登录数据存储在客户端。JWT的原则是在服务器身份验证之后,将生成一个JSON对象并将其发送回用户。之后,当用户与服务器通信时,客户在请求中发回JSON对象,服务器再重新校验。
一般JWT格式如下:

# JWT头
eyJhbGciOiJIUzI1NiJ9.
# 有效载荷
eyJ1aWQiOiJjMzU4MzBkNC1kMzZkLTQyNDAtODM3NS0yMmE1YTY0ZGUyZjkiLCJzdWIiOiJ7aWQ6MTAwLG5hbWU6YWxpfSIsInVzZXJfbmFtZSI6ImFkbWluIiwibmlja19uYW1lIjoiemhhbmdsaSIsImV4cCI6MTU4MjcwNTQzOCwiaWF0IjoxNTgyNzA1NDMyLCJqdGkiOiI4OTZlNDkxOC02NTlhLTQzYjMtYjIxYy04MmIyY2U2MDI1ZWEifQ.
# 签名
ulld0DfmDPfQqmV4t7elwmBkhRyOi0O8VPeAuVHup04
 
16204070-1de4d071acc9a1ac.png
JWT格式
  • JWT头:包含签名算法和令牌类型;
  • 有效载荷:一般包括如下数据:
"ss":发行人
"exp":到期时间
"sub":主题
"aud":用户
"nbf":在此之前不可用
"iat":发布时间
"jti":JWT ID用于标识该JWT
# 以上是7个默认字段,也可以自定义字段,如下所示
"name": "zhangli",
"root": false
  • 签名:是对上面两部分数据签名,通过指定的算法生成哈希,以确保数据不会被篡改
    理论阐述完毕,接下来我们将完成一个demo。

2、操作步骤

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.7.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.10</version>
        </dependency>
  • 在项目/src/main/resources/static文件夹下导入jquery-1.10.2.js
  • 在项目/src/main/resources/static文件夹下创建index.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>sso</title>
</head>
<body>
<h2>Hello World!</h2>
账号:<input type="text" name="name" id="name" value="ali"/>
密码:<input type="password" name="password" id="password" value="123456" />
<input type="button" value="登录" id="sub"/>
</body>
<script type="text/javascript" src="jquery-1.10.2.js"></script>
<script type="text/javascript">
    $(function(){
        $("#sub").click(function(){
            $.ajax({
                data:{name:$("#name").val(),password:$("#password").val()},
                type:"post",
                dataType:"json",
                url:"/login",
                success:function (data) {
                    if(data.status=='200')
                    {
                        window.location.href="main.html";
                    }
                    else {
                        alert(data.msg)
                    }
                }
            })
        })
    })
</script>
</html>
  • 在项目/src/main/resources/static文件夹下创建main.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
</head>
<body>
<div id="welcome"></div>欢迎你!
<h2>Hello World!</h2>

</body>
<script type="text/javascript" src="jquery-1.10.2.js"></script>
<script type="text/javascript">
    $(function(){
        $.ajax({
            data:{},
            type:"get",
            dataType:"json",
            url:"/getuser",
            success:function (data) {
                if(data.status=='200')
                {
                    $("#welcome").html(data.data);
                }
                else
                {
                    window.location.href="index.html";
                }
            }
        })
    })
</script>
</html>
  • 在主启动类同级目录下创建JwtUtil.java
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.commons.codec.binary.Base64;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class JwtUtil {
    public static String createJWT(String id, String username, long ttlMillis) throws Exception {
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        //生成JWT的时间
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        Map<String, Object> claims = new HashMap<String, Object>();
        SecretKey key = generalKey();//签名秘钥,和生成的签名的秘钥一模一样
        JwtBuilder builder = Jwts.builder()
                .setClaims(claims)
                .setId(id)
                .setIssuedAt(now)
                .setSubject(username)
                .signWith(signatureAlgorithm, key);
        if (ttlMillis >= 0) {
            long expMillis = nowMillis + ttlMillis;
            Date exp = new Date(expMillis);
            builder.setExpiration(exp);
        }
        return builder.compact();
    }

    public static Claims parseJWT(String jwt) throws Exception {
        SecretKey key = generalKey();  //签名秘钥,和生成的签名的秘钥一模一样
        Claims claims = Jwts.parser()
                .setSigningKey(key)
                .parseClaimsJws(jwt).getBody();
        return claims;
    }

    public static SecretKey generalKey() {
        String stringKey = "zhangli123";//来自数据库,不同的用户key值不同
        byte[] encodedKey = Base64.decodeBase64(stringKey);
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return key;
    }
}

  • 在主启动类同级目录下创建UserController.java
import com.alibaba.fastjson.JSONObject;
import io.jsonwebtoken.Claims;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.UUID;

@RestController
public class UserController {

    @PostMapping("/login")
    public JSONObject login(@RequestParam("name") String name, @RequestParam("password") String password, HttpServletResponse resp) throws Exception {
        JSONObject jsonObject = new JSONObject();
        if ("ali".equals(name) && "123456".equals(password)) {
            String jwt = JwtUtil.createJWT(UUID.randomUUID().toString(), name, 6000);
            Cookie cookie = new Cookie("USER_JWT", jwt);
            cookie.setPath("/");
            cookie.setMaxAge(30 * 60);
            resp.addCookie(cookie);
            jsonObject.put("status", 200);
        } else {
            jsonObject.put("status", 100);
            jsonObject.put("msg", "account or password wrong");
        }
        return jsonObject;
    }

    @GetMapping("/getuser")
    public JSONObject doGet(HttpServletRequest req) throws Exception {

        JSONObject jsonObject = new JSONObject();
        String token = "";
        Cookie[] cookies = req.getCookies();
        for (Cookie cookie : cookies) {
            if ("USER_JWT".equals(cookie.getName())) {
                token = cookie.getValue();
                break;
            }
        }
        if ("".equals(token)) {
            jsonObject.put("status", 303);
            jsonObject.put("data", "cookie当中并未携带token");
            return jsonObject;
        }
        Claims c = JwtUtil.parseJWT(token);
        jsonObject.put("status", 200);
        jsonObject.put("data", c.getSubject());
        return jsonObject;
    }
}
  • 测试
    启动主启动类,在浏览器中输入 http://localhost:8080/index.html,点击“登录”,就会进到main页面,同时也会看到登录者的名称。
    以上就是使用jwt完成登录验证。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值