master #2
@ -33,7 +33,9 @@ extra["springShellVersion"] = "3.2.3"
|
||||
|
||||
dependencies {
|
||||
implementation("org.springframework.boot:spring-boot-starter-actuator")
|
||||
implementation("org.springframework.boot:spring-boot-starter-web")
|
||||
implementation("org.springframework.boot:spring-boot-starter-web") {
|
||||
exclude("com.fasterxml.jackson.core", "jackson-core")
|
||||
}
|
||||
implementation("org.springframework.boot:spring-boot-starter-websocket")
|
||||
implementation("org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.3")
|
||||
implementation("org.springframework.shell:spring-shell-starter")
|
||||
@ -42,6 +44,10 @@ dependencies {
|
||||
// https://mvnrepository.com/artifact/commons-codec/commons-codec
|
||||
implementation("commons-codec:commons-codec:1.16.1")
|
||||
// https://mvnrepository.com/artifact/com.auth0/java-jwt
|
||||
// https://mvnrepository.com/artifact/com.alibaba.fastjson2/fastjson2
|
||||
implementation("com.alibaba.fastjson2:fastjson2:2.0.48")
|
||||
// https://mvnrepository.com/artifact/com.alibaba.fastjson2/fastjson2-extension-spring6
|
||||
implementation("com.alibaba.fastjson2:fastjson2-extension-spring6:2.0.48")
|
||||
implementation("com.auth0:java-jwt:4.4.0")
|
||||
compileOnly("org.projectlombok:lombok")
|
||||
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
||||
|
@ -0,0 +1,10 @@
|
||||
package org.mmga.clubs.annotations;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Target({ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface AuthorizationRequired {
|
||||
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
package org.mmga.clubs.commands;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.mmga.clubs.controller.UserController;
|
||||
import org.mmga.clubs.entities.BaseResponse;
|
||||
import org.mmga.clubs.entities.user.User;
|
||||
import org.mmga.clubs.entities.user.UserLoginVo;
|
||||
import org.mmga.clubs.entities.user.UserRegVo;
|
||||
import org.mmga.clubs.utils.ShellUtils;
|
||||
import org.springframework.shell.standard.ShellComponent;
|
||||
import org.springframework.shell.standard.ShellMethod;
|
||||
|
||||
@ShellComponent
|
||||
@Slf4j
|
||||
public class UserCommands {
|
||||
private final UserController userController;
|
||||
public UserCommands(UserController userController){
|
||||
this.userController = userController;
|
||||
}
|
||||
@ShellMethod("创建用户")
|
||||
public void createUser(String name, String password) {
|
||||
BaseResponse<User> user = this.userController.createUser(new UserRegVo(name, DigestUtils.md5Hex(password), 1), null);
|
||||
ShellUtils.logToResult(log, user);
|
||||
}
|
||||
@ShellMethod("登录")
|
||||
public void login(String name, String password) {
|
||||
BaseResponse<User> user = this.userController.login(new UserLoginVo(name, DigestUtils.md5Hex(password)), null);
|
||||
ShellUtils.logToResult(log, user);
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
package org.mmga.clubs.configuration;
|
||||
|
||||
import com.alibaba.fastjson2.JSONWriter;
|
||||
import com.alibaba.fastjson2.support.config.FastJsonConfig;
|
||||
import com.alibaba.fastjson2.support.spring6.http.converter.FastJsonHttpMessageConverter;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Configuration
|
||||
public class FastJsonConfiguration implements WebMvcConfigurer {
|
||||
/**
|
||||
* 配置fastjson输出格式
|
||||
**/
|
||||
@Override
|
||||
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
|
||||
// 1. 配置fastjson
|
||||
FastJsonConfig config = new FastJsonConfig();
|
||||
|
||||
config.setDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
config.setCharset(StandardCharsets.UTF_8);
|
||||
config.setWriterFeatures(
|
||||
JSONWriter.Feature.WriteNullListAsEmpty,
|
||||
//json格式化
|
||||
JSONWriter.Feature.PrettyFormat,
|
||||
//输出map中value为null的数据
|
||||
JSONWriter.Feature.WriteMapNullValue,
|
||||
//输出boolean 为 false
|
||||
JSONWriter.Feature.WriteNullBooleanAsFalse,
|
||||
//输出list 为 []
|
||||
JSONWriter.Feature.WriteNullListAsEmpty,
|
||||
//输出number 为 0
|
||||
JSONWriter.Feature.WriteNullNumberAsZero,
|
||||
//输出字符串 为 ""
|
||||
JSONWriter.Feature.WriteNullStringAsEmpty,
|
||||
//对map进行排序
|
||||
JSONWriter.Feature.SortMapEntriesByKeys
|
||||
);
|
||||
|
||||
// 2. 添加fastjson转换器
|
||||
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
|
||||
List<MediaType> supportedMediaTypes = new ArrayList<>();
|
||||
|
||||
// 3. 添加支持的媒体类型
|
||||
supportedMediaTypes.add(MediaType.APPLICATION_JSON);
|
||||
supportedMediaTypes.add(MediaType.APPLICATION_ATOM_XML);
|
||||
supportedMediaTypes.add(MediaType.APPLICATION_FORM_URLENCODED);
|
||||
supportedMediaTypes.add(MediaType.APPLICATION_OCTET_STREAM);
|
||||
supportedMediaTypes.add(MediaType.APPLICATION_PDF);
|
||||
supportedMediaTypes.add(MediaType.APPLICATION_RSS_XML);
|
||||
supportedMediaTypes.add(MediaType.APPLICATION_XHTML_XML);
|
||||
supportedMediaTypes.add(MediaType.APPLICATION_XML);
|
||||
supportedMediaTypes.add(MediaType.IMAGE_GIF);
|
||||
supportedMediaTypes.add(MediaType.IMAGE_JPEG);
|
||||
supportedMediaTypes.add(MediaType.IMAGE_PNG);
|
||||
supportedMediaTypes.add(MediaType.TEXT_EVENT_STREAM);
|
||||
supportedMediaTypes.add(MediaType.TEXT_HTML);
|
||||
supportedMediaTypes.add(MediaType.TEXT_MARKDOWN);
|
||||
supportedMediaTypes.add(MediaType.TEXT_PLAIN);
|
||||
supportedMediaTypes.add(MediaType.TEXT_XML);
|
||||
|
||||
converter.setSupportedMediaTypes(supportedMediaTypes);
|
||||
|
||||
//4 将convert添加到converters
|
||||
converter.setFastJsonConfig(config);
|
||||
converters.add(0,converter);
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package org.mmga.clubs.configuration;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.auth0.jwt.exceptions.JWTVerificationException;
|
||||
import jakarta.servlet.ServletOutputStream;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.NonNull;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.mmga.clubs.annotations.AuthorizationRequired;
|
||||
import org.mmga.clubs.entities.BaseResponse;
|
||||
import org.mmga.clubs.utils.JwtUtils;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@Configuration
|
||||
@Slf4j
|
||||
public class TokenConfiguration implements HandlerInterceptor, WebMvcConfigurer{
|
||||
private final JwtUtils jwtUtils;
|
||||
public TokenConfiguration(JwtUtils jwtUtils){
|
||||
this.jwtUtils = jwtUtils;
|
||||
}
|
||||
@Override
|
||||
public boolean preHandle(@NonNull HttpServletRequest request,@NonNull HttpServletResponse response,@NonNull Object handler) throws Exception {
|
||||
if (handler instanceof HandlerMethod handlerMethod){
|
||||
request.setAttribute("user", -1);
|
||||
String authorization = request.getHeader("Authorization");
|
||||
try{
|
||||
Integer userId = jwtUtils.verifyToken(authorization);
|
||||
request.setAttribute("user", userId);
|
||||
}catch (JWTVerificationException e){
|
||||
if (handlerMethod.hasMethodAnnotation(AuthorizationRequired.class)) {
|
||||
log.debug("用户鉴权时出现错误:", e);
|
||||
ServletOutputStream outputStream = response.getOutputStream();
|
||||
response.addHeader("Content-Encoding", "UTF-8");
|
||||
response.addHeader("Content-Type", "application/json; charset=utf-8");
|
||||
BaseResponse<Object> err = BaseResponse.failed(401, "token错误");
|
||||
outputStream.write(JSON.toJSONString(err).getBytes(StandardCharsets.UTF_8));
|
||||
outputStream.close();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(this);
|
||||
}
|
||||
}
|
@ -17,6 +17,7 @@ import org.springframework.web.bind.annotation.*;
|
||||
@RequestMapping("/api/user")
|
||||
@Tag(name = "用户", description = "用户相关接口")
|
||||
@Slf4j
|
||||
@CrossOrigin(allowCredentials = "true", allowedHeaders = {"Set-Authorization", "Authorization"}, origins = {"http://localhost:5173"})
|
||||
public class UserController {
|
||||
private final UserService service;
|
||||
@Autowired
|
||||
@ -25,12 +26,17 @@ public class UserController {
|
||||
}
|
||||
@PostMapping("/login")
|
||||
@Operation(description = "用户登录", responses = {@ApiResponse(description = "返回是否登录成功", responseCode = "200")})
|
||||
public BaseResponse<User> login(@RequestBody UserLoginVo user, HttpServletResponse response){
|
||||
public BaseResponse<Boolean> login(@RequestBody UserLoginVo user, HttpServletResponse response){
|
||||
return service.login(user, response);
|
||||
}
|
||||
@PutMapping("/create")
|
||||
@Operation(description = "创建用户", responses = {@ApiResponse(description = "返回创建后的用户")})
|
||||
public BaseResponse<User> createUser(@RequestBody UserRegVo user, HttpServletResponse response){
|
||||
public BaseResponse<Boolean> createUser(@RequestBody UserRegVo user, HttpServletResponse response){
|
||||
return service.createUser(user, response);
|
||||
}
|
||||
@GetMapping("/info")
|
||||
@Operation(description = "获取用户信息")
|
||||
public BaseResponse<User> getUserInfo(@RequestAttribute("user") int userId){
|
||||
return service.getUserInfo(userId);
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,8 @@ package org.mmga.clubs.entities;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import org.mmga.clubs.entities.user.User;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
@Schema(description = "基础返回值")
|
||||
@ -11,12 +12,15 @@ public class BaseResponse<T> {
|
||||
private int code;
|
||||
@Schema(description = "返回信息(自定义)")
|
||||
private String msg;
|
||||
@Schema(description = "响应生成时间")
|
||||
private final Date time;
|
||||
@Schema(description = "返回数据")
|
||||
private T data;
|
||||
private BaseResponse(int code, String msg, T data){
|
||||
this.code = code;
|
||||
this.msg = msg;
|
||||
this.data = data;
|
||||
this.time = new Date();
|
||||
}
|
||||
public static <T> BaseResponse<T> success(T data){
|
||||
return new BaseResponse<>(200, "接口执行成功", data);
|
||||
|
@ -3,5 +3,5 @@ package org.mmga.clubs.entities.user;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
@Schema(description = "用户登录参数类")
|
||||
public record UserLoginVo(@Schema(description = "用户名") String username,@Schema(description = "用户密码(使用MD5摘要后的hex字符串)") String password) {
|
||||
public record UserLoginVo(@Schema(description = "用户名", requiredMode = Schema.RequiredMode.REQUIRED) String username, @Schema(description = "用户密码(使用MD5摘要后的hex字符串)") String password) {
|
||||
}
|
||||
|
@ -21,16 +21,16 @@ public class UserService {
|
||||
this.jwtUtils = jwtUtils;
|
||||
}
|
||||
|
||||
public BaseResponse<User> login(UserLoginVo user, HttpServletResponse response) {
|
||||
public BaseResponse<Boolean> login(UserLoginVo user, HttpServletResponse response) {
|
||||
UserVo userVo = userDao.getUser(user.username(), DigestUtils.sha1Hex(user.password()));
|
||||
User u = packageUser(userVo);
|
||||
if (response != null && u != null){
|
||||
response.addHeader("Set-Authorization", jwtUtils.createToken(u));
|
||||
}
|
||||
return u == null ? BaseResponse.failed(404, "无效用户") : BaseResponse.success(u);
|
||||
return u == null ? BaseResponse.failed(404, "无效用户") : BaseResponse.success(true);
|
||||
}
|
||||
|
||||
public BaseResponse<User> createUser(UserRegVo user, HttpServletResponse response) {
|
||||
public BaseResponse<Boolean> createUser(UserRegVo user, HttpServletResponse response) {
|
||||
String username = user.username();
|
||||
if (userDao.countUser(username) > 0) {
|
||||
return BaseResponse.failed(409, "用户已存在");
|
||||
@ -42,7 +42,17 @@ public class UserService {
|
||||
if (newUser != null && response != null) {
|
||||
response.addHeader("Set-Authorization", jwtUtils.createToken(newUser));
|
||||
}
|
||||
return BaseResponse.success(newUser);
|
||||
return BaseResponse.success(true);
|
||||
}
|
||||
public User getUserById(int id){
|
||||
return packageUser(userDao.getUserById(id));
|
||||
}
|
||||
public User getGuestUser(){
|
||||
User user = new User();
|
||||
user.setId(-1);
|
||||
user.setName("游客");
|
||||
user.setAuth(authService.getAuthById(1));
|
||||
return user;
|
||||
}
|
||||
private User packageUser(UserVo vo) {
|
||||
if (vo == null){
|
||||
@ -54,4 +64,11 @@ public class UserService {
|
||||
result.setAuth(authService.getAuthById(vo.auth()));
|
||||
return result;
|
||||
}
|
||||
|
||||
public BaseResponse<User> getUserInfo(int userId) {
|
||||
if (userId == -1){
|
||||
return BaseResponse.success(getGuestUser());
|
||||
}
|
||||
return BaseResponse.success(getUserById(userId));
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,11 @@ package org.mmga.clubs.utils;
|
||||
|
||||
import com.auth0.jwt.JWT;
|
||||
import com.auth0.jwt.algorithms.Algorithm;
|
||||
import com.auth0.jwt.exceptions.JWTVerificationException;
|
||||
import com.auth0.jwt.interfaces.DecodedJWT;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.mmga.clubs.entities.user.User;
|
||||
import org.mmga.clubs.service.UserService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@ -25,4 +28,8 @@ public class JwtUtils {
|
||||
log.debug("对用户:{},生成JWT:{}", user.getName(), jwt);
|
||||
return jwt;
|
||||
}
|
||||
public Integer verifyToken(String token) throws JWTVerificationException {
|
||||
DecodedJWT verify = JWT.require(Algorithm.ECDSA512(ecPublicKey, ecPrivateKey)).build().verify(token);
|
||||
return verify.getClaim("uid").asInt();
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user