推荐使用 Sa-Token 集成 JWT、Swagger(使用springdoc)

1、目录结构

2、Maven

<!-- 使用 springdoc 生成 swagger 文档  -->
<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
    <version>2.5.0</version>
</dependency>

<!-- jjwt  -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.12.5</version>
</dependency>

3、JwtUtils(创建、解析token)

package com.dragon.springboot3vue3.utils;

import com.alibaba.fastjson2.JSON;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.io.Encoders;
import io.jsonwebtoken.security.Keys;
import org.springframework.stereotype.Component;
import javax.crypto.SecretKey;
import java.util.Date;
import java.util.UUID;

@Component
public class JwtUtils {
    // 设置有效期30min
    private static final long JWT_EXPIRE = 30*60*1000L;
    // 生成密钥
    private static final SecretKey secretKey = Keys.secretKeyFor(SignatureAlgorithm.HS256);
    // 密钥转 Base64 编码
    private static final String base64Key = Encoders.BASE64.encode(secretKey.getEncoded());

    /**
     * 用jwt创建token
     * @param data
     * @return
     */
    public String createToken(Object data){
        // 当前时间
        long currentTime=System.currentTimeMillis();
        // 过期时间
        long expireTime=currentTime+JWT_EXPIRE;
        // 构建jwt
        JwtBuilder builder= Jwts.builder()
                .setId(UUID.randomUUID()+"")
                .setSubject(JSON.toJSONString(data))
                .setIssuer("system")
                .setIssuedAt(new Date(currentTime))
                .signWith(secretKey,SignatureAlgorithm.HS256)
                .setExpiration(new Date(expireTime));
        return builder.compact();
    }

    /**
     * 解析token
     * @param token
     * @return
     */
    public Claims parseToken(String token){
        Claims body=Jwts.parser()
                .setSigningKey(base64Key)
                .build()
                .parseClaimsJws(token)
                .getBody();
        return body;
    }

    /**
     * 解析token,返回json格式对象
     * @param token
     * @param tClass
     * @param <T>
     * @return
     */
    public <T> T parseToken(String token,Class<T> tClass){
        Claims body=Jwts.parser()
                .setSigningKey(base64Key)
                .build()
                .parseClaimsJws(token)
                .getBody();
        return JSON.parseObject(body.getSubject(),tClass);
    }

}

4、ThreadLocalUtils(保存登录的用户信息)

package com.dragon.springboot3vue3.utils;

public class ThreadLocalUtils {
    private static final ThreadLocal THREAD_LOCAL = new ThreadLocal();

    public static <T> T get(){
        return (T) THREAD_LOCAL.get();
    }

    public static void set(Object value){
        THREAD_LOCAL.set(value);
    }

    public static void remove(){
        THREAD_LOCAL.remove();
    }
}

5、JwtInterceptor(JWT拦截器)

package com.dragon.springboot3vue3.interceptor;

import com.alibaba.fastjson2.JSON;
import com.dragon.springboot3vue3.controller.dto.RegisterOrLoginDto;
import com.dragon.springboot3vue3.entity.User;
import com.dragon.springboot3vue3.service.IUserService;
import com.dragon.springboot3vue3.utils.JwtUtils;
import com.dragon.springboot3vue3.utils.Result;
import com.dragon.springboot3vue3.utils.ThreadLocalUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

@Slf4j
@Component
public class JwtInterceptor implements HandlerInterceptor {
    @Autowired
    private JwtUtils jwtUtils;
    @Autowired
    private IUserService userService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token=request.getHeader("X-Token");
        if(token!=null){
            try {
                String username = jwtUtils.parseToken(token, RegisterOrLoginDto.class).getUsername();
                User user=userService.lambdaQuery().eq(User::getUsername,username).one();
                // 把业务数据存储到ThreadLocal(ThreadLocal 线程安全,能实现数据共享)
                ThreadLocalUtils.set(user);
                return true;
            } catch (Exception e) {
                log.error(e.toString());
            }
        }
        log.debug(request.getRequestURI()+" 验证token失败,禁止访问");
        response.setContentType("application/json;charset=utf-8");
        response.getWriter().write(JSON.toJSONString(Result.error(20000,"Token无效,请重新登录")));
        return false;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        ThreadLocalUtils.remove();
    }
}

6、SwaggerConfig(swagger配置类)

package com.dragon.springboot3vue3.config;

import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.ExternalDocumentation;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.License;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class SwaggerConfig {

    @Bean
    public OpenAPI openAPI() {
        OpenAPI openAPI = new OpenAPI();
        openAPI.info(new Info().title("前后端分离管理系统")
                               .description("使用springboot3-vue3等技术")
                               .version("v1.0.0")
                               .license(new License().name("Apache 2.0").url("https://springdoc.org")));
        openAPI.externalDocs(new ExternalDocumentation().description("项目API文档")
                                                        .url("/"));
        // 配置 Authorize 登录鉴权
        Map<String, SecurityScheme> map = new HashMap<>();
        map.put("X-Token", new SecurityScheme().type(SecurityScheme.Type.APIKEY).in(SecurityScheme.In.HEADER).name("X-Token"));
        openAPI.components(new Components().securitySchemes(map));
        map.keySet().forEach(key -> openAPI.addSecurityItem(new SecurityRequirement().addList(key)));

        return openAPI;
    }
}

7、InterceptorConfig(拦截器配置类)

package com.dragon.springboot3vue3.config;

import com.dragon.springboot3vue3.interceptor.JwtInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Autowired
    private JwtInterceptor jwtInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(jwtInterceptor)
                .excludePathPatterns(
                        "/user/register",
                        "/user/login",
                        "/user/logout",
                        "/swagger-ui/**",
                        "/v3/**"
                );
    }
}

8、Controller使用SpringDoc注解

package com.dragon.springboot3vue3.controller;

import com.dragon.springboot3vue3.controller.dto.RegisterOrLoginDto;
import com.dragon.springboot3vue3.entity.User;
import com.dragon.springboot3vue3.service.IUserService;
import com.dragon.springboot3vue3.utils.JwtUtils;
import com.dragon.springboot3vue3.utils.MD5Utils;
import com.dragon.springboot3vue3.utils.Result;
import com.dragon.springboot3vue3.utils.ThreadLocalUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

@Tag(name = "用户接口")
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private IUserService userService;
    @Autowired
    private JwtUtils jwtUtils;

    @Operation(summary = "注册")
    @PostMapping("/register")
    public Result register(@RequestBody @Validated RegisterOrLoginDto registerDto){
        User user=userService.lambdaQuery().eq(User::getUsername,registerDto.getUsername()).one();
        if(user!=null){
            return Result.error("用户名被占用,请重新输入!");
        }
        userService.register(registerDto);
        return Result.success();
    }

    @Operation(summary = "登录")
    @PostMapping("/login")
    public Result login(@RequestBody @Validated RegisterOrLoginDto loginDto){
        User user=userService.lambdaQuery().eq(User::getUsername,loginDto.getUsername()).one();

        if(user!=null && user.getPassword().equals(MD5Utils.encode(loginDto.getPassword()))){
            String token =jwtUtils.createToken(loginDto);
            return Result.success(token);
        }
        return Result.error("用户名或密码输入错误!");
    }

    @Operation(summary = "获取登录用户信息")
    @GetMapping("/userInfo")
    public Result userInfo(){
        User user = ThreadLocalUtils.get();
        return Result.success(user);
    }

    @Operation(summary = "更新用户基本信息")
    @PutMapping("/update")
    public Result update(@RequestBody User userDto){
        User user = ThreadLocalUtils.get();
        BeanUtils.copyProperties(userDto,user);
        userService.updateById(user);
        return Result.success();
    }
}

9、swagger-ui地址:http://localhost:8080/swagger-ui/index.html

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐