feat(auth): 实现用户认证模块
- 新增认证控制器 AuthController,提供登录和注册接口 - 创建 LoginForm 记录类用于接收登录/注册参数 - 定义 User 实体类映射数据库用户表结构 - 添加 UserMapper 接口继承 MyBatis-Plus 的 BaseMapper - 实现 AuthService 接口,完成登录与注册业务逻辑 - 集成 Sa-Token 框架进行权限认证管理 - 配置拦截器排除 /auth/login 路径的认证检查 - 引入 ServiceException 自定义服务异常类 - 增加全局异常处理器 ExceptionFilter 处理认证相关异常 - 创建统一响应模型 R 封装接口返回数据格式 - 在主应用类上添加 Mapper 扫描注解支持 MyBatis 映射 - 更新 application.yml 配置文件,加入数据源及 MyBatis-Plus 设置 - 修改 pom.xml 添加 MyBatis-Plus 和 Sa-Token 相关依赖项
This commit is contained in:
parent
3a35848c4f
commit
6ceab3cf86
10
pom.xml
10
pom.xml
@ -63,8 +63,14 @@
|
|||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>com.baomidou</groupId>
|
||||||
<artifactId>spring-boot-starter-security</artifactId>
|
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
|
||||||
|
<version>3.5.10.1</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.dev33</groupId>
|
||||||
|
<artifactId>sa-token-spring-boot3-starter</artifactId>
|
||||||
|
<version>1.39.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
package com.kane.animo;
|
package com.kane.animo;
|
||||||
|
|
||||||
|
import org.mybatis.spring.annotation.MapperScan;
|
||||||
|
import org.mybatis.spring.annotation.MapperScans;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
@ -8,6 +10,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|||||||
* @author Spring Boot Framework
|
* @author Spring Boot Framework
|
||||||
*/
|
*/
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
|
@MapperScan("com.kane.animo.*.mapper")
|
||||||
public class AnimoApplication {
|
public class AnimoApplication {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|||||||
@ -0,0 +1,43 @@
|
|||||||
|
package com.kane.animo.auth.controller;
|
||||||
|
|
||||||
|
import com.kane.animo.auth.domain.form.LoginForm;
|
||||||
|
import com.kane.animo.auth.service.AuthService;
|
||||||
|
import com.kane.animo.model.R;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 认证
|
||||||
|
* @author Kane
|
||||||
|
* @since 2025/11/7 14:25
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/auth")
|
||||||
|
public class AuthController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private AuthService service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录
|
||||||
|
* @param from 登录参数
|
||||||
|
* @return 登录结果
|
||||||
|
*/
|
||||||
|
@PostMapping("/login")
|
||||||
|
public R<String> login(@RequestBody LoginForm from) {
|
||||||
|
return service.login(from);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册
|
||||||
|
* @param from 注册参数
|
||||||
|
* @return 注册结果
|
||||||
|
*/
|
||||||
|
@PostMapping("/register")
|
||||||
|
public R<String> register(@RequestBody LoginForm from) {
|
||||||
|
return service.register(from);
|
||||||
|
}
|
||||||
|
}
|
||||||
55
src/main/java/com/kane/animo/auth/domain/User.java
Normal file
55
src/main/java/com/kane/animo/auth/domain/User.java
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package com.kane.animo.auth.domain;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户
|
||||||
|
* @author Kane
|
||||||
|
* @since 2025/11/07 03:10
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("user")
|
||||||
|
public class User implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID
|
||||||
|
*/
|
||||||
|
@TableId
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户名
|
||||||
|
*/
|
||||||
|
private String user;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 密码
|
||||||
|
*/
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 头像
|
||||||
|
*/
|
||||||
|
private InputStream avatar;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
|
||||||
|
}
|
||||||
14
src/main/java/com/kane/animo/auth/domain/form/LoginForm.java
Normal file
14
src/main/java/com/kane/animo/auth/domain/form/LoginForm.java
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package com.kane.animo.auth.domain.form;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录参数
|
||||||
|
* @author Kane
|
||||||
|
* @since 2025/11/7 14:28
|
||||||
|
* @param user 用户名
|
||||||
|
* @param password 密码
|
||||||
|
*/
|
||||||
|
public record LoginForm(
|
||||||
|
String user,
|
||||||
|
String password
|
||||||
|
) {
|
||||||
|
}
|
||||||
14
src/main/java/com/kane/animo/auth/mapper/UserMapper.java
Normal file
14
src/main/java/com/kane/animo/auth/mapper/UserMapper.java
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package com.kane.animo.auth.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.kane.animo.auth.domain.User;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户持久层
|
||||||
|
* @author Kane
|
||||||
|
* @since 2025/11/7 15:11
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface UserMapper extends BaseMapper<User> {
|
||||||
|
}
|
||||||
25
src/main/java/com/kane/animo/auth/service/AuthService.java
Normal file
25
src/main/java/com/kane/animo/auth/service/AuthService.java
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package com.kane.animo.auth.service;
|
||||||
|
|
||||||
|
import com.kane.animo.auth.domain.form.LoginForm;
|
||||||
|
import com.kane.animo.model.R;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 认证服务
|
||||||
|
* @author Kane
|
||||||
|
* @since 2025/11/7 14:49
|
||||||
|
*/
|
||||||
|
public interface AuthService {
|
||||||
|
/**
|
||||||
|
* 登录
|
||||||
|
* @param from 登录参数
|
||||||
|
* @return 登录结果
|
||||||
|
*/
|
||||||
|
R<String> login(LoginForm from);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册
|
||||||
|
* @param from 注册参数
|
||||||
|
* @return 注册结果
|
||||||
|
*/
|
||||||
|
R<String> register(LoginForm from);
|
||||||
|
}
|
||||||
@ -0,0 +1,67 @@
|
|||||||
|
package com.kane.animo.auth.service.impl;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.secure.BCrypt;
|
||||||
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
|
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
|
||||||
|
import com.kane.animo.auth.domain.User;
|
||||||
|
import com.kane.animo.auth.domain.form.LoginForm;
|
||||||
|
import com.kane.animo.auth.mapper.UserMapper;
|
||||||
|
import com.kane.animo.auth.service.AuthService;
|
||||||
|
import com.kane.animo.exception.ServiceException;
|
||||||
|
import com.kane.animo.model.R;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 认证服务
|
||||||
|
* @author Kane
|
||||||
|
* @since 2025/11/7 14:49
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class AuthServiceImpl implements AuthService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private UserMapper userMapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录
|
||||||
|
*
|
||||||
|
* @param from 登录参数
|
||||||
|
* @return 登录结果
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public R<String> login(LoginForm from) {
|
||||||
|
User one = new LambdaQueryChainWrapper<>(userMapper)
|
||||||
|
.eq(User::getUser, from.user())
|
||||||
|
.one();
|
||||||
|
if (one == null){
|
||||||
|
throw new ServiceException("用户不存在");
|
||||||
|
}
|
||||||
|
if (!BCrypt.checkpw(from.password(), one.getPassword())){
|
||||||
|
throw new ServiceException("密码错误");
|
||||||
|
}
|
||||||
|
StpUtil.login(one.getId());
|
||||||
|
return R.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册
|
||||||
|
*
|
||||||
|
* @param from 注册参数
|
||||||
|
* @return 注册结果
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public R<String> register(LoginForm from) {
|
||||||
|
User one = new LambdaQueryChainWrapper<>(userMapper)
|
||||||
|
.eq(User::getUser, from.user())
|
||||||
|
.one();
|
||||||
|
if (one != null){
|
||||||
|
throw new ServiceException("用户已存在");
|
||||||
|
}
|
||||||
|
User user = new User();
|
||||||
|
user.setUser(from.user());
|
||||||
|
user.setPassword(BCrypt.hashpw(from.password(), BCrypt.gensalt()));
|
||||||
|
userMapper.insert(user);
|
||||||
|
return login(from);
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/main/java/com/kane/animo/config/SaTokenConfigurer.java
Normal file
23
src/main/java/com/kane/animo/config/SaTokenConfigurer.java
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package com.kane.animo.config;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.interceptor.SaInterceptor;
|
||||||
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拦截器配置
|
||||||
|
* @author Kane
|
||||||
|
* @since 2025/11/7 16:13
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class SaTokenConfigurer implements WebMvcConfigurer {
|
||||||
|
@Override
|
||||||
|
public void addInterceptors(InterceptorRegistry registry) {
|
||||||
|
registry.addInterceptor(new SaInterceptor(handler -> StpUtil.checkLogin()))
|
||||||
|
.addPathPatterns("/**")
|
||||||
|
.excludePathPatterns("/auth/login");
|
||||||
|
// .excludePathPatterns("/auth/register");
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/main/java/com/kane/animo/exception/ServiceException.java
Normal file
12
src/main/java/com/kane/animo/exception/ServiceException.java
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package com.kane.animo.exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务异常
|
||||||
|
* @author Kane
|
||||||
|
* @since 2025/11/7 15:16
|
||||||
|
*/
|
||||||
|
public class ServiceException extends RuntimeException {
|
||||||
|
public ServiceException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/main/java/com/kane/animo/filter/ExceptionFilter.java
Normal file
25
src/main/java/com/kane/animo/filter/ExceptionFilter.java
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package com.kane.animo.filter;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.exception.NotLoginException;
|
||||||
|
import com.kane.animo.exception.ServiceException;
|
||||||
|
import com.kane.animo.model.R;
|
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异常过滤器
|
||||||
|
* @author Kane
|
||||||
|
* @since 2025/11/7 15:16
|
||||||
|
*/
|
||||||
|
@RestControllerAdvice
|
||||||
|
public class ExceptionFilter {
|
||||||
|
@ExceptionHandler(ServiceException.class)
|
||||||
|
public R<String> handleServiceException(ServiceException e) {
|
||||||
|
return R.error(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExceptionHandler(NotLoginException.class)
|
||||||
|
public R<String> handleException(NotLoginException e) {
|
||||||
|
return R.error("认证失败 - 未登录");
|
||||||
|
}
|
||||||
|
}
|
||||||
63
src/main/java/com/kane/animo/model/R.java
Normal file
63
src/main/java/com/kane/animo/model/R.java
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package com.kane.animo.model;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统一返回结果
|
||||||
|
* @author Kane
|
||||||
|
* @since 2025/11/7 14:50
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class R<T> {
|
||||||
|
private int code;
|
||||||
|
private String message;
|
||||||
|
private T data;
|
||||||
|
private long timestamp;
|
||||||
|
public static <T> R<T> success() {
|
||||||
|
R<T> r = new R<>();
|
||||||
|
r.setCode(200);
|
||||||
|
r.setMessage("success");
|
||||||
|
r.setData(null);
|
||||||
|
r.setTimestamp(System.currentTimeMillis());
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
public static <T> R<T> success(T data) {
|
||||||
|
R<T> r = new R<>();
|
||||||
|
r.setCode(200);
|
||||||
|
r.setMessage("success");
|
||||||
|
r.setData(data);
|
||||||
|
r.setTimestamp(System.currentTimeMillis());
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
public static <T> R<T> success(String message, T data) {
|
||||||
|
R<T> r = new R<>();
|
||||||
|
r.setCode(200);
|
||||||
|
r.setMessage(message);
|
||||||
|
r.setData(data);
|
||||||
|
r.setTimestamp(System.currentTimeMillis());
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
public static <T> R<T> error(String message) {
|
||||||
|
R<T> r = new R<>();
|
||||||
|
r.setCode(500);
|
||||||
|
r.setMessage(message);
|
||||||
|
r.setTimestamp(System.currentTimeMillis());
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
public static <T> R<T> build(int code, String message) {
|
||||||
|
R<T> r = new R<>();
|
||||||
|
r.setCode(code);
|
||||||
|
r.setMessage(message);
|
||||||
|
r.setTimestamp(System.currentTimeMillis());
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
public static <T> R<T> build(int code, String message, T data) {
|
||||||
|
R<T> r = new R<>();
|
||||||
|
r.setCode(code);
|
||||||
|
r.setMessage(message);
|
||||||
|
r.setData(data);
|
||||||
|
r.setTimestamp(System.currentTimeMillis());
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -4,3 +4,12 @@ server:
|
|||||||
spring:
|
spring:
|
||||||
application:
|
application:
|
||||||
name: Animo
|
name: Animo
|
||||||
|
|
||||||
|
datasource:
|
||||||
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
|
url: jdbc:mysql://localhost:3306/animo?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&allowPublicKeyRetrieval=true&useSSL=false
|
||||||
|
username: animo
|
||||||
|
password: WS6PCwksRpEYNpNt
|
||||||
|
|
||||||
|
mybatis-plus:
|
||||||
|
mapper-locations: classpath*:/mapper/**/*.xml
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user