Springboot JWT登录

本文介绍了如何在Springboot应用中使用JWT进行登录验证。JWT是一个开放标准,用于安全地传输信息。在适合需要授权和信息交换的场景中,JWT被广泛应用。文章详细讲解了JWT的原理,如何添加JWT依赖,创建自定义注释、验证器以及拦截器,并提供了相关测试和数据库密码加密的注意事项。
摘要由CSDN通过智能技术生成

JSON Web Token(JWT)?

    官方文档是这样解释的:JSON Web Token(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且独立的方式,可以在各方之间作为JSON对象安全地传输信息。此信息可以通过数字签名进行验证和信任。JWT可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对进行签名。

    虽然JWT可以加密以在各方之间提供保密,但只将专注于签名令牌。签名令牌可以验证其中包含的声明的完整性,而加密令牌则隐藏其他方的声明。当使用公钥/私钥对签署令牌时,签名还证明只有持有私钥的一方是签署私钥的一方。

    通俗来讲,JWT是一个含签名并携带用户相关信息的加密串,页面请求校验登录接口时,请求头中携带JWT串到后端服务,服务端通过签名加密串匹配校验,保证信息未被篡改。校验通过则认为是可靠的请求,将正常返回数据。

适合使用JWT的情况

    授权:这是最常见的使用场景,解决单点登录问题。因为JWT使用起来轻便,开销小,服务端不用记录用户状态信息(无状态),所以使用比较广泛;

    信息交换:JWT是在各个服务之间安全传输信息的好方法。因为JWT可以签名,例如,使用公钥/私钥对儿 - 可以确定请求方是合法的。此外,由于使用标头和有效负载计算签名,还可以验证内容是否未被篡改。

添加JWT依赖

<!-- Jwt -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.0</version>
</dependency>
<dependency>
	<groupId>com.auth0</groupId>
	<artifactId>java-jwt</artifactId>
	<version>3.4.0</version>
</dependency>

自定义注释

@UserLoginToken用来配置需要拦截器拦截的接口

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserLoginToken {
    boolean required() default true;
}

定义获取token的方法

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.example.demo.model.User;
import org.springframework.stereotype.Service;

@Service("TokenService")
public class TokenService {
    public String getToken(User user){
        String token = "";
        token = JWT.create().withAudience(user.getId()) // 将 user_id 保存到token里
                .sign(Algorithm.HMAC256(user.getPassword())); // 以 password 作为 token 的密钥
        return token;
    }
}

定义验证器

UserDao 可以参考Springboot 使用 JPA 连接 MySQL 数据库

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.example.demo.annotation.UserLoginToken;
import com.example.demo.jpaRepository.UserDao;
import com.example.demo.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;

public class AuthenticationInterceptor implements HandlerInterceptor {

	/* UserDao 可以参考JPA连接数据库:https://blog.csdn.net/weixin_45703665/article/details/103351737 */
    @Autowired
    UserDao userDao;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception {

        // 从 http 请求头中获取 token
        String token = request.getHeader("token");

        // 如果不是映射到方法 直接通过
        if (!(object instanceof HandlerMethod)) {
        	/* 如果不加这个判断,会直接拦截404报错,并返回500的报错 */
            return true;
        }
             
        HandlerMethod handlerMethod = (HandlerMethod) object;
        Method method = handlerMethod.getMethod();

        // 检查有没有需要用户权限的注释
        if (method.isAnnotationPresent(UserLoginToken.class)) {
            UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
            if (userLoginToken.required()) {
                if (token == null) {
                    response.setStatus(401);
                    return false;
                }

                String userId;
                try {
                    userId = JWT.decode(token).getAudience().get(0);
                } catch (JWTDecodeException j) {
                    response.setStatus(401); /* 返回401状态码 */
                    return false; /* 不再继续直接,直接结束进程 */
                }

                User user = userDao.findUserById(userId);
                if(user == null){
                	/* 如果没有用户信息,则返回401状态码 */
                    response.setStatus(401);
                    return false;
                }

                JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
                try{
                	/* 验证当前token是否有效 */
                    jwtVerifier.verify(token);
                }catch (JWTVerificationException e){
                    response.setStatus(401);
                    return false;
                }
                return true;
            }
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

    }
    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

    }
}

创建拦截器

拦截器请参考:Springboot 拦截器(简单版,使用session存储)

import com.example.demo.interceptor.AuthenticationInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        InterceptorRegistration addInterceptor = registry.addInterceptor(authenticationInterceptor());

        addInterceptor.addPathPatterns("/**");
    }

    @Bean
    public AuthenticationInterceptor authenticationInterceptor(){
        return new AuthenticationInterceptor();
    }
}

测试

数据库里的密码一定要加密,加密请参考Springboot BCrypt加密

import com.example.demo.annotation.UserLoginToken;
import com.example.demo.jpaRepository.UserDao;
import com.example.demo.model.User;
import com.example.demo.service.TokenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

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

@RestController
@RequestMapping("api")
public class UserController {
    @Autowired
    UserDao userDao;

    @Autowired
    TokenService tokenService;

    @PostMapping("/login")
    public Map<String, Object> login(String tel, String password){
        Map<String, Object> map = new HashMap<>();

        User user = userDao.findUserByTel(tel);
        if(user == null){
            map.put("msg", "用户不存在");
        }else{
            String token = tokenService.getToken(user);
            map.put("msg", "登录成功");
            map.put("token", token);
            map.put("user", user);
        }
        return map;
    }

    @UserLoginToken
    @GetMapping("/getMessage")
    public String getMessage(){
        return "你已通过验证";
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值