终于来到登录接口了!!!

跟着黑马程序员视频学习springboot3+vue3的学习笔记

登录的主逻辑开发

查询请求中的用户名和密码是否在数据库里(是否存在)

查询操作在注册时已经有了,所以我们只需要在UserController类里加上登录的API和处理逻辑就可以了

@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对象中密码是密文
        if(Md5Util.getMD5String(password).equals(loginUser.getPassword())){
            //登录成功
            return Result.success("jwt token 令牌..");

        }
        return Result.error("密码错误");
    }

结果

登录认证

在一个项目中,有很多controller,如果用户没有登录,是不可以查询到其他的controller接口的,这时其他接口在进行查询之前需要对用户的登录状态进行检查,这个就叫登录认证。

那我们如何实现登录认证呢?这就需要借助令牌的技术

浏览器访问登录接口,登录成功后,需要在后台生成一个令牌,并且把令牌响应给浏览器;浏览器再去访问其他接口的时候就需要把这个令牌给携带上,其他接口看到浏览器是携带令牌的并且令牌是合法有效的,就正常提供服务,否则就不提供。

令牌详解

令牌起到身份识别的作用,它就是一段字符串

令牌要求:
  • 承载业务数据, 减少后续请求查询数据库的次数

  • 需要具备防伪功能

使用JWT令牌规范
  • 全称:JSON Web Token(JSON Web Tokens - jwt.io)

  • 定义了一种简洁的,自包含的格式,用于通信双方以json数据格式安全的传输信息

以下就是Token令牌

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.JTdCJTIybmFtZSUyMiUzQSUyMlRvbSUyMiUyQyUyMmlhdCUyMiUzQTE1MTYyMzkwMjIlN0Q=.SflKxwRJSMeKKF2QT4fwpMeJf...

里面的两个“.”把Token令牌分成了三部分

  • Token组成:
    • 第一部分:Header(头) , 记录令牌类型,签名算法等 {“alg”:"HS256", "type": "JWT"} alg:加密算法,防篡改

    • 第二部分:Payload(有效载荷),携带一些自定义信息,默认信息等。这部分一定不要存放私密的数据,比如登录密码。 {“id”:"1" , "username": "Tom" }

    • 第三部分:Signature(数字签名),防止Token被篡改,确保安全性。借助header,payload部分并且加入指定密钥,通过指定签名算法计算而来。{header,payload,secret}

借助Base64编码方式把任意数据转换成64个可打印字符,所以JS字符串是通过Base64编码转换成token令牌中展示的那一段字符

JWT解析Token令牌时,会通过解密第三部分数字签名从而得到头部和载荷;再拿着解密得到的内容和用户传递的内容进行比对,不一样的话就说明数据被篡改过,不让其访问。

如何生成令牌

使用生成令牌的工具

在pom.xml文件中引人java-jwt坐标和单元测试的坐标

在src/test/java/org.exampletest下新建一个测试类JwtTest

这段代码不需要记忆,工具类里面有。但是我还不会呢,我就是跟着视频敲

package org.exampletest;

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;

import static com.mysql.cj.protocol.ExportControlled.sign;

public class JwtTest {
    @Test
    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*60*60*24))//添加过期时间
                .sign(Algorithm.HMAC256("secret"));//指定算法,配置密钥,这个密钥一定不能泄露出去,否则不能起到防篡改的作用
        System.out.println(token);
    }
}

Token的验证

在JwtTest类中添加一个验证方法

如果篡改了头部和载荷部分的数据,验证失败

如果密钥改了,验证失败

token过期了,验证失败

正式进入登录认证代码的书写

  • 添加JWT工具类

直接使用黑马程序员给我们准备好的工具类

  1. 黑马程序员教程资源下载指南 - 哔哩哔哩 (bilibili.com)搜索springboot3+vue3即可

  2. 黑马程序员领取资料的百度网盘https://pan.baidu.com/s/1w9nn_IRGcqYm1npllasjoQ&pwd=9988

  3. 不想下载的,我把代码粘过来了,如下

  4. package org.exampletest.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 = "itheima";
    	
    	//接收业务数据,生成token并返回
        public static String genToken(Map<String, Object> claims) {
            return JWT.create()
                    .withClaim("claims", claims)
                    .withExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 12))
                    .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();
        }
    
    }
    

  • 修改UserController里的登录逻辑代码实现生成令牌
   @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对象中密码是密文
        if(Md5Util.getMD5String(password).equals(loginUser.getPassword())){
            //登录成功
            Map<String,Object>claims = new HashMap<>();
            claims.put("id",loginUser.getId());
            claims.put("username",loginUser.getUsername());
            String token=JwtUtil.genToken(claims);
            return Result.success(token);
        }
        return Result.error("密码错误");
    }

令牌生成成功

  • 在其他接口实验令牌验证

 浏览器携带token再去访问其他接口,那么这个token是以什么方式携带过来的呢?以请求体还是请求头呢?

翻阅接口文档才知道

 由接口文档得出,token是请求头携带过来的,所以list方法的参数是token,参数前还要加上

@RequestHeader(name="Authorization")

在ArtitleController中修改代码为:

package org.exampletest.controller;

import jakarta.servlet.http.HttpServletResponse;
import org.exampletest.pojo.Result;
import org.exampletest.utils.JwtUtil;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@RestController
@RequestMapping("/article")
public class ArticleController {
    @RequestMapping("/list")
    public Result<String> list(@RequestHeader(name="Authorization") String token, HttpServletResponse response){
        //验证token
        try {
            Map<String,Object>claims= JwtUtil.parseToken(token);
            return Result.success("所有的文章数据......");
    }
        }catch (Exception e){
            //http响应状态码为401
            response.setStatus(401);
            return Result.error("未登录");
        }
       
}

重新运行,发送请求

失败:

 把登录成功的请求头加上:访问成功了

验证成功。 

大家会想到项目中会有很多很多controller,有非常多的接口需要令牌的校验,每个接口都写一个令牌校验的代码显然是不现实的。多个接口都有同样的技术需要完成,可以用拦截器实现。给程序注册一个拦截器,让所有的请求都经过拦截器,在拦截器里统一完成验证,验证通过才让浏览器访问接口,验证不通过就不给访问。

完了。。。。看到拦截器这里又不懂了。。。。没关系,先继续往下学,不会的先跳过,大概把这个登录认证内容,流程了解清楚后,再回过头来看拦截器的具体内容

拦截器(Interceptor)是Spring框架中用于拦截请求和响应的组件。在Spring MVC中,拦截器可以在请求处理之前、处理之后或响应发送之前执行自定义逻辑。拦截器通常用于实现诸如权限校验、日志记录、事务管理等跨多个请求的通用功能。

 在启动类所在的包下新建一个interceptors包(拦截器包),在拦截器包下建立一个登录拦截器LoginInterceptor类

 LoginInterceptor是一个自定义的拦截器,它实现了HandlerInterceptor接口。该拦截器的核心功能是在请求处理之前验证用户的令牌(Token)。如果令牌验证成功,拦截器返回true,允许请求继续执行;如果验证失败,拦截器返回false,阻止请求继续处理,并设置响应状态码为401(未授权)。

package org.exampletest.interceptors;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.exampletest.pojo.Result;
import org.exampletest.utils.JwtUtil;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import java.util.Map;

@Component
public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler){
        //令牌验证
        String token=request.getHeader("Authorization");
        //验证token
        try{
            Map<String, Object> claims= JwtUtil.parseToken(token);
            return true;
        }catch (Exception e){
            response.setStatus(401);
            //不放行
            return false;
        }
    }
}

在启动类所在的包下新建一个config包,config包下新建一个WebConfig类,用于注册拦截器

拦截器通过WebMvcConfigurer接口的addInterceptors方法进行配置。在WebConfig类中,LoginInterceptor被注入并注册到拦截器注册处。通过excludePathPatterns方法,可以指定不需要进行令牌验证的路径,例如登录和注册接口。这样配置后,只有非登录和注册的请求会经过LoginInterceptor的验证。拦截器的配置确保了只有经过验证的请求能够访问受保护的资源,从而提高了应用的安全性

package org.exampletest.config;

import org.exampletest.interceptors.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
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");

    }
}

 

 把ArticleController类中的令牌验证部分删掉

package org.exampletest.controller;

import jakarta.servlet.http.HttpServletResponse;
import org.exampletest.pojo.Result;
import org.exampletest.utils.JwtUtil;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@RestController
@RequestMapping("/article")
public class ArticleController {
    @RequestMapping("/list")
    public Result<String> list(/*@RequestHeader(name="Authorization") String token, HttpServletResponse response*/){
//        //验证token
//        try {
//            Map<String,Object>claims= JwtUtil.parseToken(token);
//            return Result.success("所有的文章数据......");
//        }catch (Exception e){
//            //http响应状态码为401
//            response.setStatus(401);
//            return Result.error("未登录");
//        }
        return Result.success("所有的文章数据......");
    }
}

到这里登录部分的接口就实现了 

  • 25
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值