JWT令牌技术解决spring博客项目的登录问题(三)

admin2024-09-11  18

5. 实现登陆

        分析

        接下来我们介绍另一种⽅案: 令牌技术

5.1 令牌技术

        令牌其实就是⼀个⽤⼾⾝份的标识, 其实本质就是⼀个字符串. ⽐如我们出⾏在外, 会带着⾃⼰的⾝份证, 需要验证⾝份时, 就掏出⾝份证⾝份证不能伪造, 可以辨别真假 ;其运行逻辑如下所示:

JWT令牌技术解决spring博客项目的登录问题(三),第1张

        服务器具备⽣成令牌和验证令牌的能⼒

        当前企业开发中, 解决会话跟踪使⽤最多的⽅案就是令牌技术.  

5.2 JWT令牌

        令牌本质就是⼀个字符串, 他的实现⽅式有很多, 我们采⽤⼀个JWT令牌来实现.

5.2.1 介绍

        JWT全称: JSON Web Token

        官⽹:JSON Web Tokens - jwt.io

        JSON Web Token(JWT)是⼀个开放的⾏业标准(RFC 7519), ⽤于客⼾端和服务器之间传递安全可靠的信息. 其本质是⼀个token, 是⼀种紧凑的URL安全⽅法.

5.2.2 JWT组成

JWT令牌技术解决spring博客项目的登录问题(三),第2张

        对上⾯部分的信息, 使⽤Base64Url 进⾏编码, 合并在⼀起就是jwt令牌 Base64是编码⽅式,⽽不是加密⽅式

5.3 JWT令牌⽣成和校验

5.3.1. 引⼊JWT令牌的依赖

 <!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-api -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-api</artifactId>
            <version>0.11.5</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-impl -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-impl</artifactId>
            <version>0.11.5</version>
            <scope>runtime</scope>
        </dependency>
        
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-jackson</artifactId>
            <!-- or jjwt-gson if Gson is preferred -->
            <version>0.11.5</version>
            <scope>runtime</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-api -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-api</artifactId>
            <version>0.11.5</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-impl -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-impl</artifactId>
            <version>0.11.5</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-jackson</artifactId>
            <!-- or jjwt-gson if Gson is preferred -->
            <version>0.11.5</version>
            <scope>runtime</scope>
        </dependency>

5.3.2. JWT令牌的⽣成和校验

        使⽤Jar包中提供的API来完成JWT令牌的⽣成和校验,代码如下:

package com.example.spring_blog_24_9_8;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.io.Encoders;
import io.jsonwebtoken.security.Keys;
import org.junit.jupiter.api.Test;

import javax.crypto.SecretKey;
import java.security.Key;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class JWTUtisTest {
    //过期时间: 1小时的毫秒数
    private final static long EXPIRATION_DATE = 60 * 60 * 1000;
    //秘钥
    private final static String secretString = "MDDOvlf8sQ675YuOsOxu45EXYK1dl/PoAyJq9C8vta0=";
    //生成安全秘钥
    private final static Key key = Keys.hmacShaKeyFor(Decoders.BASE64.decode(secretString));

    //生成令牌
    @Test
    public void genToken(){
        //⾃定义信息
        Map<String, Object> claim = new HashMap<>();
        claim.put("userId", 1);
        claim.put("userName", "shenmengyao");

        String token = Jwts.builder()
                .setClaims(claim) //⾃定义内容(负载
                .setExpiration(new Date(System.currentTimeMillis()+EXPIRATION_DATE))//设置过期时间
                .signWith(key)
                .compact();
        System.out.println(token);
    }

    //生成key
    @Test
    public void genKey(){
        //生成key
        SecretKey secretKey = Keys.secretKeyFor(SignatureAlgorithm.HS256);
        String encode = Encoders.BASE64.encode(secretKey.getEncoded());
        System.out.println(encode);
    }
    //校验令牌
    @Test
    public void parseToken(){
        String token = "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6InNoZW5tZW5neWFvIiwidXNlcklkIjoxLCJleHAiOjE3MjU5NTMzMTl9.Aa7kKOH6vJFo09eEYCLtGCqRh6lBiXucRh-ze-F-IY8";
        JwtParser build = Jwts.parserBuilder().setSigningKey(key).build();
        Claims body = null;
        try {
            body = build.parseClaimsJws(token).getBody();
        } catch (Exception e) {
            System.out.println("令牌校验失败");
        }
        System.out.println(body);
    }
}

生成密钥: 

JWT令牌技术解决spring博客项目的登录问题(三),第3张

生成令牌:

JWT令牌技术解决spring博客项目的登录问题(三),第4张

我们把⽣成的令牌通过官⽹进⾏解析, 就可以看到我们存储的信息了:

JWT令牌技术解决spring博客项目的登录问题(三),第5张

令牌校验:

JWT令牌技术解决spring博客项目的登录问题(三),第6张         令牌解析后, 我们可以看到⾥⾯存储的信息,如果在解析的过程当中没有报错,就说明解析成功了. 令牌解析时, 也会进⾏时间有效性的校验, 如果令牌过期了, 解析也会失败. 修改令牌中的任何⼀个字符, 都会校验失败, 所以令牌⽆法篡改

        学习令牌的使⽤之后, 接下来我们通过令牌来完成⽤⼾的登录

        1. 登陆⻚⾯把⽤⼾名密码提交给服务器.

        2. 服务器端验证⽤⼾名密码是否正确, 如果正确, 服务器⽣成令牌, 下发给客⼾端.

        3. 客⼾端把令牌存储起来(⽐如Cookie, local storage等), 后续请求时, 把token发给服务器

        4. 服务器对令牌进⾏校验, 如果令牌正确, 进⾏下⼀步操作

JWT令牌技术解决spring博客项目的登录问题(三),第7张

JWT令牌技术解决spring博客项目的登录问题(三),第8张 5.4 约定前后端交互接⼝

[请求]
/user/login
username=shenmengyao&password=111111
[响应] 
{
 "code": 200,
 "msg": "",
 "data": 
"eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJ6aGFuZ3NhbiIsImlhdCI6MTY5ODM5N
zg2MCwiZXhwIjoxNjk4Mzk5NjYwfQ.oxup5LfpuPixJrE3uLB9u3q0rHxxTC8_AhX1QlYV--E"
}
//验证成功, 返回token, 验证失败返回 ""

5.5 实现服务器代码

        创建JWT⼯具类 

package com.example.spring_blog_24_9_8.utils;


import com.example.spring_blog_24_9_8.constants.Constant;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;

import java.security.Key;
import java.util.Date;
import java.util.Map;

@Slf4j
public class JwtUtils {
    //过期时间: 1小时的毫秒数
    private final static long EXPIRATION_DATE = 600000 * 60 * 1000;
    private final static String secretString = "2LT0G2Og7zG9aOPJR+Y4cLMGicyUoRVPS4J0xRlpZzg=";
    private final static Key key = Keys.hmacShaKeyFor(Decoders.BASE64.decode(secretString));

    //生成令牌
    public static String genToken(Map<String, Object> claim){
       return Jwts.builder()
                .setClaims(claim)
                .setExpiration(new Date(System.currentTimeMillis()+EXPIRATION_DATE))
                .signWith(key)
                .compact();
    }
    /**
     * 解析令牌
     * @param token
     * @return
     */
    public static Claims parseToken(String token){
        JwtParser build = Jwts.parserBuilder().setSigningKey(key).build();
        Claims body = null;
        try {
            body = build.parseClaimsJws(token).getBody();
        } catch (ExpiredJwtException e) {
            log.error("token过期, 校验失败, token:",token);

        } catch (Exception e) {
            log.error("token校验失败, token:",token);
        }
        return body;
    }
    //校验令牌
    public static boolean checkToken(String token){
        Claims body = parseToken(token);
        if (body==null){
            System.out.println("555");
            return false;
        }
        System.out.println("444");
        return true;
    }
    public static Integer getUserIdFromToken(String token){
        Claims body = parseToken(token);
        if (body!=null){
            System.out.println("222");
            return (Integer) body.get("id");

        }
        System.out.println("111");
        return null;
    }
}

创建 UserController

package com.example.spring_blog_24_9_8.controller;

import com.example.spring_blog_24_9_8.model.Result;
import com.example.spring_blog_24_9_8.model.User;
import com.example.spring_blog_24_9_8.service.UserService;
import com.example.spring_blog_24_9_8.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@RequestMapping("/user")
@RestController
public class UserController {
    @Autowired
    private UserService userService;
    @RequestMapping("/login")
    public Result login(HttpServletRequest request, HttpServletResponse
            response, String username, String password){
        if (!StringUtils.hasLength(username) ||
                !StringUtils.hasLength(password)){
            log.error("username:"+username+",password:"+password);
            return Result.fail(-1, "⽤⼾名或密码为空");
        }
        //判断账号密码是否正确
        User user = userService.getUserInfo(username);
        if (user==null || !user.getPassword().equals(password)){
            return Result.fail(-1, "⽤⼾名或密码错误");
        }
        //登录成功, 返回token
        Map<String , Object> claims = new HashMap<>();
        claims.put("id", user.getId());
        claims.put("username", user.getUserName());
        String token = JwtUtils.genToken(claims);
        System.out.println("⽣成token:"+ token);
        return Result.success(token);
    }
}

userservice:

@Service
public class UserService {
 @Autowired
 private UserMapper userMapper;
 public User getUserInfo(String username){
      return userMapper.selectByName(username);
 }
}

5.6 实现客⼾端代码

        修改 login.html, 完善登录⽅法 前端收到token之后, 保存在localstorage中

function login() {
            //发送ajax请求, 获得token
            $.ajax({
                type:"post",
                url: "/user/login",
                data:{
                    "username": $("#username").val(),
                    "password": $("#password").val()
                },
                success:function(result){
                    if(result.code==200 && result.data != ""){
                        //存储token
                        localStorage.setItem("user_token", result.data);
                        location.href = "blog_list.html";
                    }else{  // 自行补充完整
                        alert("用户名或密码错误")
                    }
                }
            });
        }

local storage相关操作

        运行服务器,访问登录页面,输入正确的账号和密码:

JWT令牌技术解决spring博客项目的登录问题(三),第9张

JWT令牌技术解决spring博客项目的登录问题(三),第10张

        输入错误的账号和密码,查看返回的结果:

JWT令牌技术解决spring博客项目的登录问题(三),第11张

本文的内容到这里就结束了,谢谢观看!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明原文出处。如若内容造成侵权/违法违规/事实不符,请联系SD编程学习网:675289112@qq.com进行投诉反馈,一经查实,立即删除!