1

在 SpringBoot 项目中简单实现 JWT 验证 - 拉克斯文

 1 year ago
source link: https://www.cnblogs.com/loogn/p/17099338.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

在 SpringBoot 项目中简单实现 JWT 验证

使用 SpringBoot 提供 api 的时候,我更喜欢使用 jwt 的方式来做验证。网上有会多 Spring Security 整合 jwt 的,也有 Shiro 整合 jwt 的,感觉有点复杂。这里分享一下自己在项目中的简单实现。

除了 SpringBoot 基本的依赖,需要一个生成 jwt 和序列化的包。生成 jwt 的包依赖很多,因为我项目里使用了 hutool 这个包,就只用用它了。

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.9</version>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.9</version>
        </dependency>

jwt用户模型

定义一个 Jwt 的 sub 字段模型,存储用户:

import lombok.Data;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

@Data
public class JwtUser {
    /**
     * 用户编号
     */
    private Integer id;
    /**
     * 用户名
     */
    private String name;
    /**
     * 角色
     */
    private String role;

    /**
     * 获取当前请求用户
     * @return
     */
    public static JwtUser getCurrentUser() {

        RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
        return (JwtUser) request.getAttribute("user");
    }
}

定义一个用于请求类和方法的注解

import java.lang.annotation.*;

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

    /**
     * 是否匿名可以访问
     * @return
     */
    boolean anonymous() default false;

    /**
     * 角色
     * @return
     */
    String[] roles() default {};
}

JWT 帮助类

用于生成 jwt 和 解析 JwtUser 对象。


import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTUtil;
import cn.hutool.jwt.signers.JWTSigner;
import cn.hutool.jwt.signers.JWTSignerUtil;
import com.google.gson.Gson;
import com.mpyf.xapi.security.JwtUser;
import lombok.var;

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

public class JwtTokenUtils {

    public static final String SECRET = "your_secret";
    public static final String ISS = "com.your.cn";

    private static final int EXPIRATIONHOURS = 24; //过期时间24小时
    
    //创建token
    public static String createToken(JwtUser user) {
        return createToken(user, EXPIRATIONHOURS);
    }

    public static String createToken(JwtUser user, int hours) {
        String subJson = new Gson().toJson(user);
        JWTSigner jwtSigner = JWTSignerUtil.hs512(SECRET.getBytes());

        JWT jwt = JWT.create().setSigner(jwtSigner);
        jwt
                .setJWTId(UUID.randomUUID().toString().replace("-", ""))
                .setSubject(subJson) //用户信息
                .setIssuer(ISS)      //签发者
                //.setAudience("受众")
                //.setNotBefore(new Date())
                .setIssuedAt(new Date())
                .setExpiresAt(new Date(System.currentTimeMillis() + hours * 3600 * 1000));
        return jwt.sign();
    }

    public static JwtUser getUser(String token) {

        if (StringHelper.isNullOrEmpty(token)) return null;

        var jwt = JWTUtil.parseToken(token);
        JWTSigner jwtSigner = JWTSignerUtil.hs512(SECRET.getBytes());
        jwt.setSigner(jwtSigner);

        if (jwt.validate(10)) {
            var subJson = jwt.getPayload("sub").toString();
            JwtUser user = new Gson().fromJson(subJson, JwtUser.class);
            return user;
        } else {
            return null;
        }
    }
}

验证拦截器

定义jwt的验证拦截器,从请求头获取 token 解析并验证。

import com.mpyf.xapi.helper.JwtTokenUtils;
import com.mpyf.xapi.helper.StringHelper;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;

/**
 * jwt 验证拦截器
 */
@Component
public class JwtAuthInterceptor implements HandlerInterceptor {

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

        //Authorization:Bearer+空格+token
        String token = request.getHeader("Authorization");
        if (token != null) {
            token = token.replace("Bearer ", "");
        }
        //处理模拟登录的jwt
        if (StringHelper.isNullOrEmpty(token)) {
            token = request.getParameter("jwt");
        }
        if (StringHelper.isNullOrEmpty(token)) {
            //兼容从请求参数传token
            Object jwt = request.getAttribute("jwt");
            if (jwt != null) {
                token = jwt.toString();
            }
        }
        JwtUser user = JwtTokenUtils.getUser(token);
        request.setAttribute("user", user);

        if (handler instanceof HandlerMethod) {
            HandlerMethod h = (HandlerMethod) handler;
            Authorize authorize = h.getMethodAnnotation(Authorize.class);
            if (authorize == null) {
                authorize = h.getMethod().getDeclaringClass().getAnnotation(Authorize.class);
            }

            //如果没有Authorize或者可以匿名访问,直接返回
            if (authorize != null && !authorize.anonymous()) {
                {
                    if (user == null) {
                        response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
                        return false;
                    } else if (authorize.roles() != null && authorize.roles().length > 0 &&
                            Arrays.stream(authorize.roles()).allMatch(s -> !s.equalsIgnoreCase(user.getRole()))) {
                        //没权限
                        response.sendError(HttpServletResponse.SC_FORBIDDEN);
                        return false;
                    }
                }
            }
        }
        return true;
    }
}

注册拦截器

在 WebMvc 配置中注册拦截器,并支持跨域请求

import com.mpyf.xapi.security.JwtAuthInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.*;


@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    JwtAuthInterceptor jwtAuthInterceptor;


    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(jwtAuthInterceptor).addPathPatterns("/api/**");
        WebMvcConfigurer.super.addInterceptors(registry);
    }

    @Override
    public void addCorsMappings(CorsRegistry registry) {

        registry.addMapping("/api/**")
                .allowedOriginPatterns("*")
                .allowedMethods("*")
                .allowedHeaders("*")
                //.maxAge(3600)
                .allowCredentials(true);
        WebMvcConfigurer.super.addCorsMappings(registry);
    }
}

Controller中使用

@RestController
@RequestMapping("/api/test")
@Authorize(roles = {"admin", "user"})
public class TestController {

    @GetMapping("admin_and_user")
    public String admin_and_user(){
        return "admin 和 user 角色都可以访问";
    }
    @GetMapping("admin_only")
    @Authorize(roles = "admin") //覆盖Controller的设置
    public String admin_only(){
        return "只有 admin 角色可以访问";
    }
    @GetMapping("public_all")
    @Authorize(anonymous = true) 
    public String public_all(){
        return "匿名可以访问";
    }
}

不用 Spring Security 和 Shiro ,是不是更简单呢!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK