Java中使用 jwt + 拦截器配置 超级详细教程(详细源码)

博客围绕Java开发展开,介绍了创建JWT的方法,包括导入Maven和Gradle依赖、创建JWT工具类;还说明了配置拦截器的步骤,如配置拦截器文件和创建拦截器类;此外,创建了PassToken和UserLoginToken两个注解,并讲解了注解的使用及解析。

一、创建JWT

1.导入依赖

maven

<!--引入JWT依赖,由于是基于Java,所以需要的是java-jwt-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.4.0</version>
        </dependency>

gradle

implementation 'io.jsonwebtoken:jjwt:0.9.1'

implementation 'com.auth0:java-jwt:3.4.0'

2.创建jwt工具类

package com.zx.framework.util;

import com.zx.cargo.pojo.User;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

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

/**
 * jwt工具类 生成、解析、校验token
 */
public class JwtUtil {
	/**
     * 用户登录成功后生成jwt
     * 使用Hs256算法
     * 三部分组成 头部+荷载+签证信息
     * @param ttlMillis jwt过期时间
     * @param user      登录成功的user对象
     * @return
     */
    public static String createJwt(long ttlMillis, User user){
        // header部分,jwt已经封装好了
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

        // jwt生成时间 当前时间
        long nowMillis = System.currentTimeMillis();
        Date date = new Date(nowMillis);

        // payload 荷载部分(存放有效信息的地方,包含标准中注册的声明、公共声明、私有声明)
        // 创建私有声明
        Map<String,Object> claims = new HashMap<>();
        claims.put("id", user.getUserId());
        claims.put("username",user.getUserName());
        claims.put("password",user.getUserPassword());

        // 生成秘钥secret用
//        String key = user.getUserPassword();
        byte[] bytes = user.getUserPassword().getBytes();
        // 生成签发人
        String subject = user.getUserName();

        // 为payload添加标准声明和私有声明(new一个JwtBuilder,设置jwt的body)
        JwtBuilder jwtBuilder = Jwts.builder()
                // 先设置自己创建的私有声明,要是写在标准声明后面,会覆盖掉标准声明
                .setClaims(claims)
                // 设置jti(jwt id),主要用来作为一次性token,从而回避重放攻击
                .setId(UUID.randomUUID().toString())
                // 设置iat jwt签发时间
                .setIssuedAt(date)
                // 设置jwt的所有人
                .setSubject(subject)
                // 设置签名使用的签名算法和签名使用的秘钥
                .signWith(signatureAlgorithm, bytes);
        // 设置jwt的过期时间
        if(ttlMillis>= 0){
            long expMillis = ttlMillis+nowMillis;
            Date expDate = new Date(expMillis);
            jwtBuilder.setExpiration(expDate);
        }
        System.out.println("生成jwt");
        return jwtBuilder.compact();
    }

	/**
     * 解密jwt
     * @param token 需要被解密的token
     * @param user 用户的对象
     * @return
     */
    public static Claims parseJWT(String token,User user){
        // 签名秘钥(与生成签名的秘钥一样)
        String key = user.getUserPassword();

        // 得到DefaultJwtParser
        Claims claims = Jwts.parser()
                // 设置签名的秘钥
                .setSigningKey(key)
                // 设置需要解析的jwt
                .parseClaimsJws(token).getBody();
        return claims;
    }


	/**
     * 校验jwt
     * 判断token携带的密码跟数据库里的是否一致(也可用官方的校验方法)
     * @param token
     * @param user
     * @return
     */
    public static Boolean isVerify(String token,User user){
        // 秘钥
        byte[] bytes = user.getUserPassword().getBytes();

        // 得到DefaultJwtParser
        Claims claims = Jwts.parser()
                // 设置签名的秘钥
                .setSigningKey(bytes)
                // 设置需要解析的jwt
                .parseClaimsJws(token)
                .getBody();

        System.out.println("claims-----》"+claims);

        // 判断密码是否一致
        if(claims.get("password").equals(user.getUserPassword())){
            return true;
        }

        return false;
    }
}

二、配置拦截器

1.配置拦截器文件

package com.zx.framework.config;

import com.zx.framework.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;

/**
 * jwt 后台访问拦截
 * 拦截器配置文件 config
 */
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Bean
    public AuthenticationInterceptor authenticationInterceptor() {
        return new AuthenticationInterceptor();
    }
    // 这个方法用来注册拦截器,我们自己写好的拦截器需要通过这里添加注册才能生效
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //注册TestInterceptor拦截器
        InterceptorRegistration registration = registry.addInterceptor(authenticationInterceptor());
        registration.addPathPatterns("/**");//所有路径都被拦截
        registration.excludePathPatterns("" +
                "/assets/**",             // assets文件夹里文件不拦截
                "/**/*.js",              //js静态资源不拦截
                "/**/*.css"             //css静态资源不拦截
        );
    }
}

2.创建拦截器类

package com.zx.framework.interceptor;

import com.auth0.jwt.JWT;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.zx.cargo.pojo.User;
import com.zx.cargo.stock.service.UserLoginService;
import com.zx.framework.annotation.PassToken;
import com.zx.framework.annotation.UserLoginToken;
import com.zx.framework.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
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 {

    @Autowired
    private UserLoginService userLoginService;
    /*
    controller执行之前
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object){
        // 从http请求头中取出token
        System.out.println("输出token"+request.getHeader("token"));
        String token = request.getHeader("token");
        // 如果不是映射到方法直接通过
        if(!(object instanceof HandlerMethod)){
            return true;
        }

        HandlerMethod handlerMethod = (HandlerMethod) object;
        Method method = handlerMethod.getMethod();
        // 方法是否带有UserLoginToken注释
        if(method.isAnnotationPresent(UserLoginToken.class)){
            UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
            if(userLoginToken.required()){
                return true;
            }
        }

        // 方法是否带有PassToken注释
        if(method.isAnnotationPresent(PassToken.class)){
            PassToken passToken = method.getDeclaredAnnotation(PassToken.class);
            if(passToken.required()){
                // 执行认证
                if(token == null){
                    throw new RuntimeException("无token,请重新登录!");
                }
                // 获取token中的userId
                String userId;
                try {
                    userId = JWT.decode(token).getClaim("id").asString();
                } catch (JWTDecodeException j){
                    throw new RuntimeException("访问异常!");
                }
                User user = userLoginService.findUserById(userId);
                if(StringUtils.isEmpty(user)){
                    throw new RuntimeException("当前用户不存在,请重新登录!");
                }
                Boolean verify = JwtUtil.isVerify(token, user);
                if(!verify){
                    throw new RuntimeException("非法访问!");
                }
                return true;
            }
        }
        return true;
    }
    /*
    controller执行后,页面渲染前
     */
    @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 {

    }
}

三、创建使用注解

创建两个注解,PassToken和UserLoginToken,用于在项目开发中,如果需要权限校验就标注userlogintoken,如果访问的资源不需要权限验证则正常编写不需要任何注解,如果用的的请求时登录操作,在用户登录的方法上增加passtoken注解。

1.除登录外使用的注解

package com.zx.framework.annotation;

import java.lang.annotation.*;

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PassToken {
    boolean required() default true;
}

2.登录使用的注解

ackage com.zx.framework.annotation;

import java.lang.annotation.*;

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

注解解析:从上面我们新建的两个类上我们可以看到主要的等学习到的就四点
第一:如何创建一个注解
第二:在我们自定义注解上新增@Target注解(注解解释:这个注解标注我们定义的注解是可以作用在类上还是方法上还是属性上面)
第三:在我们自定义注解上新增@Retention注解(注解解释:作用是定义被它所注解的注解保留多久,一共有三种策略,SOURCE 被编译器忽略,CLASS 注解将会被保留在Class文件中,但在运行时并不会被VM保留。这是默认行为,所有没有用Retention注解的注解,都会采用这种策略。RUNTIME 保留至运行时。所以我们可以通过反射去获取注解信息。
第四:boolean required() default true; 默认required() 属性为true

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

对应方法加入对应注解即可

Spring Cloud + JWT + MybatisPlus 的组合为构建微服务架构的后端系统提供了一套完整的解决方案。在这种架构下,Token登录是常见的用户认证方式,它通过生成Token作为身份凭证,避免了在服务间传递用户信息,增强了安全性。以下是一个简化的教程,用于说明如何使用这些技术实现Token登录: 1. **项目依赖和配置**: 首先,你需要在项目的`pom.xml`文件中添加Spring Cloud、JWT、MybatisPlus等相关依赖。这里仅列举关键依赖,具体版本根据实际需求选择: ```xml <!-- Spring Boot Starter --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <!-- Spring Cloud Starter --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!-- Mybatis Plus --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.x.x</version> </dependency> <!-- JWT --> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> ``` 2. **配置JWT**: 在`application.yml`或`application.properties`中配置JWT的密钥和Token过期时间: ```yaml jwt: secret: your-secret-key expiration: 3600000 # Token过期时间,单位毫秒 ``` 3. **创建Token生成和验证工具类**: 使用JWT提供的工具类来生成和验证Token。通常会创建一个`JwtUtil`类来完成这个任务: ```java @Component public class JwtUtil { // 获取Token的工具方法 public static String generateToken(String username) { // 使用HS512算法和密钥生成Token return Jwts.builder() .setSubject(username) .setExpiration(new Date(System.currentTimeMillis() + expiration)) .signWith(SignatureAlgorithm.HS512, secret) .compact(); } // 验证Token的工具方法 public static Claims getClaimsFromToken(String token) { try { return Jwts.parser() .setSigningKey(secret) .parseClaimsJws(token) .getBody(); } catch (Exception e) { return null; } } } ``` 4. **创建用户认证接口**: 实现一个基于Token的用户认证接口,该接口接收用户名和密码,验证通过后返回Token: ```java @RestController public class AuthController { @PostMapping("/login") public ResponseEntity<?> createAuthenticationToken(@RequestBody LoginUserDto loginUserDto) { // 这里简化了用户验证和密码加密的过程,实际开发中应使用安全的密码验证方式 if (userExistsAndValid(loginUserDto.getUsername(), loginUserDto.getPassword())) { String token = JwtUtil.generateToken(loginUserDto.getUsername()); return ResponseEntity.ok(new JwtResponse(token)); } else { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid credentials"); } } private boolean userExistsAndValid(String username, String password) { // 检查用户是否存在和密码是否正确 return true; } } ``` 5. **创建Token验证过滤器**: 创建一个过滤器,用于在请求到达Controller前拦截并验证Token的有效性: ```java @Component public class JwtTokenFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // 从请求头中获取Token String token = request.getHeader("Authorization"); if (token != null && !token.isEmpty()) { try { // 验证Token Claims claims = JwtUtil.getClaimsFromToken(token.replace("Bearer ", "")); // 如果验证成功,则将用户名设置到请求中供后续使用 request.setAttribute("username", claims.getSubject()); filterChain.doFilter(request, response); } catch (Exception e) { response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid Token"); } } else { response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Token is missing"); } } } ``` 6. **配置Spring Security**: 配置Spring Security来使用JWT过滤器,并设置认证规则: ```java @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private JwtTokenFilter jwtTokenFilter; @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .antMatchers("/login").permitAll() .anyRequest().authenticated(); // 添加JWT过滤器 http.addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class); } } ``` 以上是基于Spring Cloud + JWT + MybatisPlus 实现Token登录的一个大致流程。在实际开发中,你需要根据具体业务逻辑完善用户服务、密码验证和用户管理等环节。此外,源码通常涉及到整个项目的结构和业务逻辑,这里无法提供完整的源码,但上述步骤可作为实现Token登录的核心指导。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

国家级著名CV工程师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值