- 完善注册功能
- 完善全局异常处理器
This commit is contained in:
yulinling 2025-06-19 21:53:33 +08:00
parent 8937ec945a
commit 7fa1a38e33
9 changed files with 177 additions and 21 deletions

View File

@ -56,8 +56,8 @@ public class SecurityConfig {
) )
// 认证请求 // 认证请求
.authorizeHttpRequests(auth -> auth .authorizeHttpRequests(auth -> auth
.requestMatchers("/login").permitAll() .requestMatchers("/auth/**").permitAll()
// .requestMatchers("/users", "/users/**").hasRole("ADMIN") .requestMatchers("/auth/register").permitAll()
.anyRequest().access((authenticationSupplier, requestAuthorizationContext) -> { .anyRequest().access((authenticationSupplier, requestAuthorizationContext) -> {
HttpServletRequest request = requestAuthorizationContext.getRequest(); HttpServletRequest request = requestAuthorizationContext.getRequest();
Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

View File

@ -1,15 +1,18 @@
package asia.yulinling.workflow.controller; package asia.yulinling.workflow.controller;
import asia.yulinling.workflow.dto.request.LoginRequest; import asia.yulinling.workflow.dto.request.LoginRequest;
import asia.yulinling.workflow.dto.request.RegisterRequest;
import asia.yulinling.workflow.dto.response.JWTAuthResponse; import asia.yulinling.workflow.dto.response.JWTAuthResponse;
import asia.yulinling.workflow.model.ApiResponse; import asia.yulinling.workflow.model.ApiResponse;
import asia.yulinling.workflow.service.AuthService; import asia.yulinling.workflow.service.AuthService;
import asia.yulinling.workflow.utils.ResponseUtil; import asia.yulinling.workflow.utils.ResponseUtil;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
/** /**
@ -22,16 +25,27 @@ import org.springframework.web.bind.annotation.RestController;
*/ */
@RestController @RestController
@RequiredArgsConstructor @RequiredArgsConstructor
@RequestMapping("/auth")
@Slf4j @Slf4j
public class AuthController { public class AuthController {
private final AuthService authenticate; private final AuthService authService;
@PostMapping("/login") @PostMapping("/login")
public ApiResponse<JWTAuthResponse> login(@RequestBody LoginRequest loginRequest) { public ApiResponse<JWTAuthResponse> login(@RequestBody LoginRequest loginRequest) {
String token = authenticate.login(loginRequest); String token = authService.login(loginRequest);
JWTAuthResponse jwtAuthResponse = new JWTAuthResponse(token); JWTAuthResponse jwtAuthResponse = new JWTAuthResponse(token);
return ApiResponse.ofSuccess(jwtAuthResponse); return ApiResponse.ofSuccess(jwtAuthResponse);
} }
@PostMapping("/logout")
public ApiResponse<?> logout(HttpServletRequest request) throws SecurityException {
return authService.logout(request);
}
@PostMapping("/register")
public ApiResponse<?> register(@RequestBody RegisterRequest registerRequest) throws Exception {
return authService.register(registerRequest);
}
} }

View File

@ -0,0 +1,65 @@
package asia.yulinling.workflow.dto.request;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
/**
* <p>
* 注册请求类
* </p>
*
* @author YLL
* @since 2025/6/19
*/
@Data
public class RegisterRequest {
/**
* 用户名
*/
@NotBlank(message = "用户名不能为空")
private String username;
/**
* 昵称
*/
private String nickname;
/**
* 密码
*/
@NotBlank(message = "密码不能为空")
private String password;
/**
* 二次密码
*/
@NotBlank(message = "确认密码不能为空")
private String confirmPassword;
/**
* 电子邮箱
*/
@Email
private String email;
/**
* 生日
*/
private String birthday;
/**
* 性别
*/
private Integer sex;
/**
* 手机号
*/
@Max(11)
@Min(5)
private String phone;
}

View File

@ -1,9 +1,12 @@
package asia.yulinling.workflow.exception.handler; package asia.yulinling.workflow.exception.handler;
import asia.yulinling.workflow.exception.BaseException;
import asia.yulinling.workflow.exception.JsonException; import asia.yulinling.workflow.exception.JsonException;
import asia.yulinling.workflow.exception.PageException; import asia.yulinling.workflow.exception.PageException;
import asia.yulinling.workflow.model.ApiResponse; import asia.yulinling.workflow.model.ApiResponse;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
@ -30,9 +33,9 @@ public class GlobalExceptionHandler {
*/ */
@ExceptionHandler(Exception.class) @ExceptionHandler(Exception.class)
@ResponseBody @ResponseBody
public ApiResponse<?> jsonErrorHandler(JsonException e) { public ApiResponse<?> cathAllErrorHandler(Exception e) {
log.error(e.getMessage()); log.error("json error:{}", e.getMessage());
return ApiResponse.ofException(e); return ApiResponse.of(500, e.getMessage(), null);
} }
/** /**

View File

@ -56,7 +56,7 @@ public class ApiResponse<T> {
* @param data 返回数据 * @param data 返回数据
* @return ApiResponse * @return ApiResponse
*/ */
public static <T> ApiResponse<T> of(Integer code, String message, T data) { public static <T> ApiResponse<T> of(Integer code, String message, Object data) {
return new ApiResponse<>(code, message, data); return new ApiResponse<>(code, message, data);
} }
@ -109,7 +109,7 @@ public class ApiResponse<T> {
* @param <T> {@link BaseException} 子类 * @param <T> {@link BaseException} 子类
* @return ApiResponse * @return ApiResponse
*/ */
public static <T extends BaseException> ApiResponse<T> ofException(T t, T data) { public static <T extends BaseException> ApiResponse<T> ofException(T t, Object data) {
return of(t.getCode(), t.getMessage(), data); return of(t.getCode(), t.getMessage(), data);
} }

View File

@ -5,6 +5,7 @@ import asia.yulinling.workflow.model.ApiResponse;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -20,6 +21,7 @@ import java.io.IOException;
* @since 2025/6/13 * @since 2025/6/13
*/ */
@Component @Component
@Slf4j
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint { public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
private final ObjectMapper objectMapper; private final ObjectMapper objectMapper;
@ -31,6 +33,8 @@ public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
public void commence(HttpServletRequest request, HttpServletResponse response, public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException { AuthenticationException authException) throws IOException {
log.error("AuthenticationException: {}",authException.getMessage());
// 1. 设置res 401 // 1. 设置res 401
response.setContentType("application/json;charset=UTF-8"); response.setContentType("application/json;charset=UTF-8");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);

View File

@ -1,6 +1,9 @@
package asia.yulinling.workflow.service; package asia.yulinling.workflow.service;
import asia.yulinling.workflow.dto.request.LoginRequest; import asia.yulinling.workflow.dto.request.LoginRequest;
import asia.yulinling.workflow.dto.request.RegisterRequest;
import asia.yulinling.workflow.model.ApiResponse;
import jakarta.servlet.http.HttpServletRequest;
/** /**
* <p> * <p>
@ -12,4 +15,8 @@ import asia.yulinling.workflow.dto.request.LoginRequest;
*/ */
public interface AuthService { public interface AuthService {
String login(LoginRequest loginRequest); String login(LoginRequest loginRequest);
ApiResponse<?> logout(HttpServletRequest request) throws SecurityException;
ApiResponse<?> register(RegisterRequest request) throws Exception;
} }

View File

@ -1,15 +1,29 @@
package asia.yulinling.workflow.service.impl; package asia.yulinling.workflow.service.impl;
import asia.yulinling.workflow.constant.Status;
import asia.yulinling.workflow.dto.request.LoginRequest; import asia.yulinling.workflow.dto.request.LoginRequest;
import asia.yulinling.workflow.dto.request.RegisterRequest;
import asia.yulinling.workflow.mapper.UserMapper;
import asia.yulinling.workflow.model.ApiResponse;
import asia.yulinling.workflow.model.entity.User;
import asia.yulinling.workflow.service.AuthService; import asia.yulinling.workflow.service.AuthService;
import asia.yulinling.workflow.utils.JwtUtil; import asia.yulinling.workflow.utils.JwtUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.Date;
/** /**
* <p> * <p>
@ -26,6 +40,8 @@ public class AuthServiceImpl implements AuthService {
private final AuthenticationManager authenticationManager; private final AuthenticationManager authenticationManager;
private final JwtUtil jwtUtil; private final JwtUtil jwtUtil;
private final PasswordEncoder passwordEncoder;
private final UserMapper userMapper;
@Override @Override
public String login(LoginRequest loginRequest) { public String login(LoginRequest loginRequest) {
@ -33,8 +49,55 @@ public class AuthServiceImpl implements AuthService {
loginRequest.getUsername(), loginRequest.getPassword() loginRequest.getUsername(), loginRequest.getPassword()
)); ));
SecurityContextHolder.getContext().setAuthentication(authentication); SecurityContextHolder.getContext().setAuthentication(authentication);
String token =jwtUtil.generateToken(authentication, false); String token = jwtUtil.generateToken(authentication, false);
log.info("generateToken: {}", token); log.info("generateToken: {}", token);
return token; return token;
} }
@Override
public ApiResponse<?> logout(HttpServletRequest request) throws SecurityException {
try {
jwtUtil.invalidateToken(request);
} catch (SecurityException e) {
log.info("invalidateToken: {}", e.getMessage());
}
return ApiResponse.ofStatus(Status.LOGOUT);
}
@Override
public ApiResponse<?> register(RegisterRequest request) throws Exception {
if (!StrUtil.equals(request.getConfirmPassword(), request.getPassword())) {
log.info("password {}, confirmPassword {}", request.getConfirmPassword(), request.getPassword());
return ApiResponse.of(400, "密码不一致", null);
}
if (userMapper.exists(new LambdaQueryWrapper<User>().eq(User::getUsername, request.getUsername()))) {
return ApiResponse.of(400, "用户名已存在", null);
}
if (userMapper.exists(new LambdaQueryWrapper<User>().eq(User::getEmail, request.getEmail()))) {
return ApiResponse.of(400, "邮箱已注册", null);
}
User user = new User();
user.setUsername(request.getUsername());
user.setPassword(passwordEncoder.encode(request.getPassword()));
user.setEmail(request.getEmail());
user.setNickname(request.getNickname());
user.setPhone(request.getPhone());
user.setBirthday(request.getBirthday());
user.setStatus(1);
user.setUpdateTime(new Date());
log.info("User: {}", user);
try {
userMapper.insert(user);
} catch (Exception e) {
log.error("insertUser Error: {}", e.getMessage());
throw new RuntimeException(e);
}
return ApiResponse.of(200, "注册成功", null);
}
} }

View File

@ -1,18 +1,18 @@
DROP TABLE IF EXISTS `wk_user`; DROP TABLE IF EXISTS `wk_user`;
CREATE TABLE `wk_user` CREATE TABLE `wk_user`
( (
`id` bigint(64) NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '主键', `id` bigint(64) NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '主键',
`username` VARCHAR(32) NOT NULL UNIQUE COMMENT '用户名', `username` VARCHAR(32) NOT NULL UNIQUE COMMENT '用户名',
`nickname` VARCHAR(32) NOT NULL UNIQUE COMMENT '昵称', `nickname` VARCHAR(32) DEFAULT '默认用户' COMMENT '昵称',
`password` VARCHAR(256) NOT NULL COMMENT '加密后的密码', `password` VARCHAR(256) NOT NULL COMMENT '加密后的密码',
`email` VARCHAR(32) NOT NULL UNIQUE COMMENT '邮箱', `email` VARCHAR(32) UNIQUE COMMENT '邮箱',
`birthday` DATETIME DEFAULT NULL COMMENT '生日', `birthday` DATETIME DEFAULT NULL COMMENT '生日',
`sex` INT(2) DEFAULT NULL COMMENT '性别,男-1,女-2', `sex` INT(2) DEFAULT NULL COMMENT '性别,男-1,女-2',
`phone` VARCHAR(15) DEFAULT NULL UNIQUE COMMENT '手机号', `phone` VARCHAR(15) DEFAULT NULL UNIQUE COMMENT '手机号',
`status` INT(2) NOT NULL DEFAULT 1 COMMENT '状态 -1删除 0警用 1启用', `status` INT(2) NOT NULL DEFAULT 1 COMMENT '状态 -1删除 0警用 1启用',
`create_time` DATETIME NOT NULL DEFAULT NOW() COMMENT '创建时间', `create_time` DATETIME NOT NULL DEFAULT NOW() COMMENT '创建时间',
`update_time` DATETIME NOT NULL DEFAULT NOW() COMMENT '上次更新时间', `update_time` DATETIME NOT NULL DEFAULT NOW() COMMENT '上次更新时间',
`last_login_time` DATETIME DEFAULT NULL COMMENT '上次登录时间' `last_login_time` DATETIME DEFAULT NULL COMMENT '上次登录时间'
) ENGINE = INNODB ) ENGINE = INNODB
DEFAULT CHARSET = UTF8 COMMENT '用户表'; DEFAULT CHARSET = UTF8 COMMENT '用户表';