Java基于JWT实现Token登录验证+自定义异常

一、JWT是什么?

1、先看利用token登录的实现流程:

1、客户端使用账号密码登录
2、服务端接受到请求验证账号和密码
3、服务端验证请求后,会生成一个token,并把token返回给客户端
4、客户端收到token,并存储起来,在每次请求的时候带上这个token,可以在head中携带
5、服务端接受到请求后,验证token,如果验证成功,则返回请求数据

2、JWT介绍:

JWT代表JSON Web Token,是一种用于在网络上安全地传输信息的开放标准(RFC 7519)。它以紧凑和自包含的方式表示信息,通常用于在用户和服务之间传递身份验证和授权信息。JWT由三部分组成:头部(Header)、载荷(Payload)和签名(Signature)。

3、JWT的验证流程:

1、解析Token: 首先,将收到的JWT分割成头部、载荷和签名三个部分。这个过程通常会使用编程语言中相应的库或函数来完成。

2、验证头部和签名: 获取JWT中的头部和载荷信息后,根据头部中指定的加密算法和使用的密钥(通常是一个密钥或公钥)对头部和载荷进行签名验证。验证过程会使用相同的算法和密钥来生成签名,并将生成的签名与JWT中的签名进行比较,以确认JWT的完整性和真实性。

3、检查有效期和其他声明: JWT中的载荷部分可能包含了诸如令牌的过期时间(Expiration Time)等声明。在验证过程中,需要检查这些声明以确保令牌在有效期内,并可能进行其他额外的声明验证,如角色、权限等。

4、可选的其他验证步骤: 根据需求,还可以进行其他的验证步骤,比如验证令牌是否在黑名单中,是否与存储的用户信息匹配等。

JWT的验证流程主要依赖于对JWT规范和实现的理解,以及使用的编程语言或框架提供的相应功能。在验证JWT时,确保密钥的安全性是非常重要的,因为泄露密钥可能会导致令牌被篡改或伪造。

二、使用步骤

1.user数据库(代码演示是配置在yml文件上)

在这里插入图片描述

2.项目结构

在这里插入图片描述

3.相关依赖

  <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot</artifactId>
            <version>2.5.2</version>
        </dependency>

        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.18.2</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.48</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.0</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.1</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.83</version>
        </dependency>
    </dependencies>

4.代码

User:

@Data
@TableName("user")
public class User {
    @TableId(type = IdType.AUTO)
    private Long id;

    @TableField(value = "user_name")
    private String userName;

    @TableField(value = "password")
    private String password;
}


UserDao:

@Mapper
public interface UserDao extends BaseMapper<User> {
}

UserService:


public interface UserService {

    User loing(String userName, String userPassword);

    List<User> getUserList();

}

UserServiceImpl:

@DS("master")
@Service
public class UserServiceImpl implements UserService {

    @Resource
    private UserDao userDao;

    @Value("${login.userName:admin}")
    private String name;
    @Value("${login.password:123456}")
    private String password;

    @Override
    public User loing(String userName, String userPassword) {
//        //查表方式
//        Map<String, Object> map = new HashMap<>();
//        map.put("user_name", userName);
//        map.put("password", userPassword);
//        List<User> users = userDao.selectByMap(map);
//        if (CollectionUtils.isEmpty(users)){
//            return null;
//        }
//        return users.get(0);
        if (Objects.equals(userName,name) && Objects.equals(userPassword,password)){
            User user = new User();
            user.setUserName(name);
            user.setPassword(password);
            return user;
        }
        return null;
    }

    @Override
    public List<User> getUserList() {
        return userDao.selectList(null);
    }

}

TokenUtil:

public class TokenUtil {
    /**
     * 设置延期的时间 x分钟
     * x*1000*60
     */
    private static final long EXPIR_DATE = 60000;
//    private static final long EXPIR_DATE = 3600000;

    /**
     * 设置token的秘钥
     */
    private static final String TOKEN_SECRET = "kb_resource_qwe12300czx";


    /**
     * 获取token
     *
     * @param userName
     * @param passWord
     * @return
     */
    public static String getTokenSecret(String userName, String passWord) {
        String token = "";
        try {
            // 过期时间
            Date date = new Date(System.currentTimeMillis() + EXPIR_DATE);
            // 设置头部信息
            Map<String, Object> head = new HashMap<>();
            head.put("typ", "JWT");
            head.put("alg", "HS256");

            // 设置秘钥
            Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);

//            JWT.create().setHeader()
            // 生成签名

            token = JWT.create()
                    .withHeader(head)
                    .withClaim("userName", userName)
                    .withClaim("passWord", passWord)
                    .withExpiresAt(date)
                    .sign(algorithm);
        } catch (Exception e) {
           throw new RuntimeException(e);
        }
        return token;
    }

    /**
     * 验证token
     *
     * @param token
     * @return
     */
    public static DecodedJWT verify(String token) {
            Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
            JWTVerifier verifier = JWT.require(algorithm).build();
            return verifier.verify(token);
    }
}

全局异常&自定义异常

@Setter
@Getter
public class MeException extends Exception {
    private String msg;
    private int code = 500;
    private Map<String, Object> map;

    public MeException(String msg) {
        super(msg);
        this.msg = msg;
    }

    public MeException(String msg, Throwable cause) {
        super(msg, cause);
        this.msg = msg;
    }

    public MeException(String msg, int code) {
        super(msg);
        this.msg = msg;
        this.code = code;
    }

    public MeException(String msg, int code, Map<String, Object> map) {
        super(msg);
        this.msg = msg;
        this.code = code;
        this.map = map;
    }

}
@RestControllerAdvice
public class GlobalExceptionHandler
{
    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);


    /**
     * 业务异常
     */
    @ExceptionHandler(MeException.class)
    public Object handleServiceException(MeException e, HttpServletRequest request)
    {
        log.error(e.getMessage(), e);
        return ApiResponse.error(e.getMessage());
    }

}

ApiResponse:

@Data
public class ApiResponse {
    private int status;
    private String msg;
    private Object data;

    public ApiResponse(int status, String msg, Object data) {
        this.status = status;
        this.msg = msg;
        this.data = data;
    }

    // Getters and Setters

    // Optional: You can also add static methods to create common response objects.
    public static ApiResponse success(Object data) {
        return new ApiResponse(200, "Success", data);
    }

    public static ApiResponse error(String message) {
        return new ApiResponse(500, message, null);
    }
}

LoginController:

@Controller
@RequestMapping("/login")
public class LoginController {


    @Resource
    private UserService userService;

    @RequestMapping("/toLogin")
    @ResponseBody
    public ApiResponse toLogin(String userName, String userPassword, HttpServletResponse response){
        User user = userService.loing(userName, userPassword);
        JSONObject jsonObject = new JSONObject();

        if(user == null){
            return ApiResponse.error("账号或者密码错误,请重试!");
        }

        String token = TokenUtil.getTokenSecret(userName, userPassword);
        jsonObject.put("token", token);
        response.setHeader("token", token);
        Cookie cookie = new Cookie("token", token);
        cookie.setPath("/");
        response.addCookie(cookie);

        return ApiResponse.success(jsonObject);
    }

    @UserLoginToken
    @RequestMapping("/list")
    @ResponseBody
    public ApiResponse list(){
        List<User> userList = userService.getUserList();
        return  ApiResponse.success(userList);
    }
//    @UserLoginToken
    @RequestMapping("/test")
    @ResponseBody
    public ApiResponse test(){
        return  ApiResponse.success("请求成功");
    }

}

UserLoginToken(自定义注解)

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserLoginToken {

    boolean required() default true;
}

LoginrequiredInterceptor

@Component
public class LoginrequiredInterceptor implements HandlerInterceptor {

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

        String token = request.getHeader("token");

        if(handler instanceof HandlerMethod){
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();

            if (method.isAnnotationPresent(UserLoginToken.class)) {
                UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);

                if(userLoginToken.required()){
                    if(token == null){
                        throw new RuntimeException("token为空,请重新登录");
                    }else{
                        //查询用户是否存在
                        // 验证token
                        try {
                            TokenUtil.verify(token);
                        } catch (Exception e) {
                            if (e instanceof TokenExpiredException || e instanceof InvalidClaimException) {
                                // 触发刷新AccessToken的操作
                                throw new MeException("登录令牌鉴权失败,令牌超时");
                            } else {
                                // 签名失效的场景
                                throw new MeException("登录令牌鉴权失败," + e.getMessage());
                            }
                        }
                        return  true;
                    }
                }
            }
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }
}

WebMvcConfig

@Component
public class WebMvcConfig implements WebMvcConfigurer {

    @Resource
    private LoginrequiredInterceptor loginrequiredInterceptor;


    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginrequiredInterceptor)
                .excludePathPatterns("/login/toLogin")
                .addPathPatterns("/**");
    }

}

三、测试

1.token过期时间校验,自定义异常

在这里插入图片描述
在这里插入图片描述

  • 16
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Spring Boot是一个基于Spring框架的快速开发框架,JWT(Json Web Token)是一种认证和授权的机制。结合Spring Boot和JWT,我们可以实现基于Token登录权限认证。 首先,我们需要引入相关依赖,包括Spring Boot Starter Web、Spring Security和jjwtJava JWT库)等。 接下来,我们可以创建一个用户实体类,用于存储用户信息,包括用户名和密码等。同时,我们还需要创建一个用于生成Token的工具类。 在Spring Boot中,我们可以使用Spring Security来配置登录认证和权限控制。可以创建一个继承自WebSecurityConfigurerAdapter的类,并通过@EnableWebSecurity注解开启Spring Security的配置。 在配置类中,我们可以重写configure方法,来配置登录验证和权限控制。可以通过使用用户名和密码进行登录验证,并通过生成的Token进行权限控制。 在用户登录成功后,我们可以使用工具类生成Token,并将其返回给前端。前端在后续的请求中可以将Token带上,放在请求头中或者放在请求的参数中。 在后续的接口中,可以使用Spring Security的注解对接口进行权限控制,例如使用@PreAuthorize注解来标注只有特定权限的用户才能访问该接口。 在接口中,可以通过解析Token,并验证其合法性,来进行用户的权限认证。 总结起来,使用Spring Boot结合JWT可以简单快速地实现基于Token登录权限认证。通过自定义工具类生成Token和通过Spring Security的配置来实现登录认证和权限控制,可以保证接口的安全性和可控性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值