Springboot 从零开始 3 json web token / annotation / interceptor 的使用

本文介绍了Springboot中使用JSON Web Token (JWT) 进行身份验证,包括生成和验证token,创建自定义注解以及实现拦截器来处理需要登录的接口。详细讲述了注解的使用和拦截器如何在运行时识别并处理请求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

验证登录

登录的校验对比加盐的密钥是否匹配即可,正常项目中尽量不要明文发送密码。
首先简单确认是否登录成功:

@PostMapping("/login")
    public Map<String, Object> login(@RequestBody Map<String, Object> params) throws Exception {
        Map<String, Object> map = ETools.responseMap();
        String username = params.get("username").toString();
        String password = params.get("password").toString();
        List<EUser> list = mapper.getUserBy(username);
        if (list.size() > 0) {
            EUser user = list.get(0);
            if (ETools.verifyMd5(password, salt, user.getPassword())) {
                return  map;
            }
        }
        map.put("status", 0);
        map.put("msg", "login failure");
        return map;
    }
/**
     * MD5验证方法
     *
     * @param text 明文
     * @param key 密钥
     * @param md5 密文
     * @return true/false
     * @throws Exception
     */
    public static boolean verifyMd5(String text, String key, String md5) throws Exception {
        //根据传入的密钥进行验证
        String md5Text = md5(text, key);
        if(md5Text.equalsIgnoreCase(md5))
        {
            System.out.println("MD5验证通过");
            return true;
        }

        return false;
    }

生成token

添加JWT依赖(json web token)

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

使用JWT生成token

	public String getToken(EUser user) {
        String token = JWT.create()
                .withAudience(String.valueOf(user.getId()))
                .sign(Algorithm.HMAC256(user.getPassword()));
        return token;
    }

使用JWT验证token

	public EUser getUser(String token) {
        long uid;
        try {
            uid = Long.parseLong(JWT.decode(token).getAudience().get(0).toString());
        } catch (JWTDecodeException e) {
            throw new RuntimeException("401");
        }
        EUser user = mapper.getOne(uid).get(0);
        JWTVerifier verifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
        try {
            verifier.verify(token);
        } catch (JWTVerificationException e) {
            throw new RuntimeException("401");
        }
        return user;
    }

添加运行时可识别的注解

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

@Target:注解的作用目标
@Target(ElementType.TYPE) 接口、类、枚举、注解
@Target(ElementType.FIELD) 字段、枚举的常量
@Target(ElementType.METHOD) 方法
@Target(ElementType.PARAMETER) 方法参数
@Target(ElementType.CONSTRUCTOR) 构造函数
@Target(ElementType.LOCAL_VARIABLE) 局部变量
@Target(ElementType.ANNOTATION_TYPE) 注解
@Target(ElementType.PACKAGE) 包

@Retention:注解的保留位置
RetentionPolicy.SOURCE:这种类型的Annotations只在源代码级别保留,编译时就会被忽略,在class字节码文件中不包含。
RetentionPolicy.CLASS:这种类型的Annotations编译时被保留,默认的保留策略,在class文件中存在,但JVM将会忽略,运行时无法获得。
RetentionPolicy.RUNTIME:这种类型的Annotations将被JVM保留,所以他们能在运行时被JVM或其他使用反射机制的代码所读取和使用。
@Document:说明该注解将被包含在javadoc中
@Inherited:说明子类可以继承父类中的该注解

拦截请求

增加接口添加上面自定义的注解@RequiredToken表示该接口需要登录

	@RequiredToken
    @RequestMapping("/getMessage")
    public Map<String, Object> getMessage(@RequestBody Map<String, Object> params) throws Exception {
        Map<String, Object> map = ETools.responseMap();
        map.put("msg", "return message string");
        return map;
    }

新建一个配置类,通过@Configuration注释告诉Spring将此类作为配置类bean添加到IOC容器中,spring2.0需要实现WebMvcConfigurer的方式重写addInterceptors,在方法里返回一个合适的配置类的实例用作配置。

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authenticationInterceptor())
                .addPathPatterns("/**");

    }

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

}

新建一个配置类AuthenticationInterceptor实现HandlerInterceptor接口。preHandle方法会在请求处理前进行拦截,在此方法中调用method类的isAnnotationPresent检查对应的注解是否是自定义注解,并在method的getAnnotation方法中获取注解类的方法完成自定义的逻辑判断结果。使用上面getUser方法进行token有效性校验。

public class AuthenticationInterceptor implements HandlerInterceptor {

    @Autowired
    private EUserMapper mapper;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("token");
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        Method method = ((HandlerMethod) handler).getMethod();
        //skip pass token type
        if (method.isAnnotationPresent(PassToken.class)) {
            if (method.getAnnotation(PassToken.class).required()) {
                return true;
            }
        }
        //check the validity of the token
        if (method.isAnnotationPresent(RequiredToken.class)) {
            if (!method.getAnnotation(RequiredToken.class).required()) {
                return true;
            }
            if (token == null) {
                throw new RuntimeException("请先登录");
            }
            EUser user = getUser(token);
        }
        return true;
    }

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

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {

    }

    public EUser getUser(String token) {
        long uid;
        try {
            uid = Long.parseLong(JWT.decode(token).getAudience().get(0).toString());
        } catch (JWTDecodeException e) {
            throw new RuntimeException("401");
        }
        EUser user = mapper.getOne(uid).get(0);
        JWTVerifier verifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
        try {
            verifier.verify(token);
        } catch (JWTVerificationException e) {
            throw new RuntimeException("401");
        }
        return user;
    }

}

运行项目,使用postman进行检查,先在登录接口中增加token的返回:

@PostMapping("/login")
    public Map<String, Object> login(@RequestBody Map<String, Object> params) throws Exception {
        Map<String, Object> map = ETools.responseMap();
        String username = params.get("username").toString();
        String password = params.get("password").toString();
        List<EUser> list = mapper.getUserBy(username);
        if (list.size() > 0) {
            EUser user = list.get(0);
            if (ETools.verifyMd5(password, salt, user.getPassword())) {
                String token = getToken(user);
                Map<String, String> data = new HashMap<String, String>();
                data.put("token", token);
                map.put("data", data);
                return map;
            }
        }
        map.put("status", 0);
        map.put("msg", "login failure");
        return map;
    }

调用登录接口获取token:
在这里插入图片描述
调用getMessage接口进行测试:

使用错误的token返回:
在这里插入图片描述
不使用token返回:
在这里插入图片描述
使用正确token返回:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值