【SpringBoot实战篇】登录认证

🍀🌸明确需求--接口文档--思路分析--开发--测试🌸🍀💕

1 明确需求

2 接口文档

登录

3 思路分析

UserServic、UserMapper在注册的时候已经实现

现在我们重点看UserController 控制器

4 开发(实现)

4.1 登录认证 JWT token介绍

描述: 就是在未登录之前,访问某些页面内容,会被拦截,显示未登录,请登录等字样

**全称:**JSON Web Token (https://jwt.io/)

**简介:**定义了一种简洁欸的、自包含的格式,用于通信对方以json数据格式安全的传输信息

**解决:**使用JWT token令牌

**过程:**用户登录成功后后台生成一个令牌,并将令牌响应给浏览器;浏览器访问其他页面都必须携带上这个令牌,才能访问成功

优点:

  • 承载业务数据, 减少后续请求查询数据库的次数
  • 防篡改, 保证信息的合法性和有效性

4.2实现

4.2.1编写token测试

导入依赖--->在src/test/java... 下编写测试文件

pom.xml

复制代码
  <!--java-jwt坐标-->
    <dependency>
      <groupId>com.auth0</groupId>
      <artifactId>java-jwt</artifactId>
      <version>4.4.0</version>
    </dependency>

    <!--单元测试的坐标 起步依赖-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
    </dependency>

编写测试

测试token的生成以及对token的解析

复制代码
package com.aaa;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.junit.jupiter.api.Test;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class JwtTest {

    //@Test
    //生成token
    public void testGen() { 
        Map<String, Object> claims = new HashMap<>();
        claims.put("id", 1);
        claims.put("username", "张三");
        //生成jwt的代码
        String token = JWT.create()
                .withClaim("user", claims)//添加载荷
                .withExpiresAt(new Date(System.currentTimeMillis() + 1000))//添加过期时间
                .sign(Algorithm.HMAC256("aaa"));//指定算法,配置秘钥,随便写

        System.out.println(token);//打印出token形式

    }

    //@Test
    //解析token
    public void testParse() {
        //定义字符串,模拟用户传递过来的token
        String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjp7ImlkIjoxLCJ1c2VybmFtZSI6IuW8oOS4iSJ9LCJleHAiOjE2OTQzMjUzMzB9.dFmeOG04w6EfnCue4CFS-x-XMRv145EfsY8wnchbxL4";//这是上面生成的token内容,需正确,否则不能解析成功

        JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("aaa")).build(); //密钥与上面需保持一致

        DecodedJWT decodedJWT = jwtVerifier.verify(token);//验证token,生成一个解析后的JWT对象
        Map<String, Claim> claims = decodedJWT.getClaims();
        System.out.println(claims.get("user")); //{"id":1,"username":"张三"}

        //如果篡改了头部和载荷部分的数据,那么验证失败
        //如果秘钥改了,验证失败
        //token过期
    }
}

4.3 登录认证使用token

4.3.1工具类 --JwtUtil.java

复制代码
package com.aaa.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;

import java.util.Date;
import java.util.Map;

public class JwtUtil {

    private static final String KEY = aaa"; //密钥
	
	//接收业务数据,生成token并返回
    public static String genToken(Map<String, Object> claims) {
        return JWT.create()
                .withClaim("claims", claims)
                .withExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60 * 60 ))
                .sign(Algorithm.HMAC256(KEY));
    }

	//接收token,验证token,并返回业务数据
    public static Map<String, Object> parseToken(String token) {
        return JWT.require(Algorithm.HMAC256(KEY))
                .build()
                .verify(token)
                .getClaim("claims")
                .asMap();
    }

}

4.3.2登录控制器

  • 对用户名、密码进行判断

  • 用户名、密码无误后,生成令牌

    @PostMapping("/login")
    public Result<String> login(@Pattern(regexp = "^\S{5,16}") String username, @Pattern(regexp = "^\\S{5,16}") String password) {
    //根据用户名查询用户
    User loginUser = userService.findByUserName(username);
    //判断该用户是否存在
    if (loginUser == null) {
    return Result.error("用户名错误");
    }

    复制代码
          //判断密码是否正确  loginUser对象中的password是密文
          if (Md5Util.getMD5String(password).equals(loginUser.getPassword())) {
              //登录成功,生成令牌
              Map<String, Object> claims = new HashMap<>();
              claims.put("id", loginUser.getId()); //放入注册用户的id
              claims.put("username", loginUser.getUsername());//放入注册用户的username
              String token = JwtUtil.genToken(claims); //生成token
              return Result.success(token); //响应JWT token令牌字符串
          }
          return Result.error("密码错误");
      }

4.4 设置拦截器(因为拦截的页面可能不止一个)

拦截:将对未登录的用户,其操作的访问某些页面进行拦截,不允许未登录用户访问

拦截器

获取浏览器请求头中的token(Authorization里的内容)

(查看存储的token是否存在或者失效)

解析token 放行返回true

否则不放行,放回false

复制代码
package com.aaa.interceptors;

import com.aaa.pojo.Result;
import com.aaa.utils.JwtUtil;
import com.aaa.utils.ThreadLocalUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import java.util.Map;

@Component
public class LoginInterceptor implements HandlerInterceptor {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //令牌验证
        String token = request.getHeader("Authorization");
        //验证token
        try {
            //从redis中获取相同的token
            ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
            String redisToken = operations.get(token);
            if (redisToken==null){
                //token已经失效了
                throw new RuntimeException();
            }
            Map<String, Object> claims = JwtUtil.parseToken(token);

            //把业务数据存储到ThreadLocal中
            ThreadLocalUtil.set(claims);
            //放行
            return true;
        } catch (Exception e) {
            //http响应状态码为401
            response.setStatus(401);
            //不放行
            return false;
        }
    }
配置类

public void addInterceptors(InterceptorRegistry registry) {: 这是一个公共方法,名为**addInterceptors** ,它接受一个**InterceptorRegistry** 类型的参数**registry** 。**InterceptorRegistry**是Spring MVC提供的一个用于注册拦截器的类

复制代码
package com.aaa.config;

import com.itheima.interceptors.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private LoginInterceptor loginInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //登录接口和注册接口不拦截
        registry.addInterceptor(loginInterceptor).excludePathPatterns("/user/login","/user/register");
    }
}
相关推荐
Y学院4 分钟前
Spring AI Alibaba 高质量实战教程(从入门到企业级落地)
java·人工智能·spring·自然语言处理
水木流年追梦9 分钟前
CodeTop Top 300 热门题目3-字符串相加
java·前端·算法
编程之升级打怪10 分钟前
自定义实现Java的HashMap集合
java·开发语言
后端AI实验室12 分钟前
我带的那个实习生,比我更依赖AI——但他的问题和我完全不同
java·ai
y小花13 分钟前
安卓StorageManagerService
android·java
码王吴彦祖14 分钟前
AI 逆向分析国航 AirChina FECU 参数来源并实现离线生成
android·java·javascript
LJianK114 分钟前
进程、线程、多线程、异步
java·开发语言·jvm
ch.ju18 分钟前
Java程序设计(第3版)第二章——循环结构1
java
大黄烽18 分钟前
IDEA中集成AI 工具CodeBuddy和Trae区别和选型
java·人工智能·intellij-idea
HalvmånEver21 分钟前
MySQL表的约束(二)
java·数据库·mysql