- * STOMP 不是一个传输协议,而是一个简单的文本消息协议,定义消息格式和交换规则 + * WebSocket 配置 * * @author haoxr - * @since 2.3.0 + * @since 2.4.0 */ @Configuration @ConditionalOnProperty(name = "system.config.websocket-enabled")// system.config.websocket-enabled = true 才会自动装配 @EnableWebSocketMessageBroker // 启用WebSocket消息代理功能和配置STOMP协议,实现实时双向通信和消息传递 +@RequiredArgsConstructor public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { + private final AuthChannelInterceptor authChannelInterceptor; /** - * 配置和注册WebSocket端点(endpoints) + * 注册一个端点,客户端通过这个端点进行连接 */ @Override public void registerStompEndpoints(StompEndpointRegistry registry) { @@ -35,9 +39,7 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { /** - * 配置消息代理(Message Broker) - *
- * 设置消息传输的规则和前缀
+ * 配置消息代理
*/
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
@@ -51,4 +53,14 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
registry.setUserDestinationPrefix("/user");
}
+
+ /**
+ * 配置客户端入站通道拦截器
+ *
+ * @param registration 通道注册器
+ */
+ @Override
+ public void configureClientInboundChannel(ChannelRegistration registration) {
+ registration.interceptors(authChannelInterceptor);
+ }
}
diff --git a/src/main/java/com/youlai/system/controller/AuthController.java b/src/main/java/com/youlai/system/controller/AuthController.java
index 91be7665..d24df45d 100644
--- a/src/main/java/com/youlai/system/controller/AuthController.java
+++ b/src/main/java/com/youlai/system/controller/AuthController.java
@@ -1,41 +1,26 @@
package com.youlai.system.controller;
-import cn.hutool.core.util.StrUtil;
import com.youlai.system.common.constant.SecurityConstants;
import com.youlai.system.common.result.Result;
-import com.youlai.system.common.util.RequestUtils;
-import com.youlai.system.security.captcha.EasyCaptchaService;
import com.youlai.system.model.dto.CaptchaResult;
import com.youlai.system.model.dto.LoginResult;
-import com.youlai.system.security.JwtTokenManager;
-import io.jsonwebtoken.Claims;
+import com.youlai.system.service.AuthService;
+import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
-import io.swagger.v3.oas.annotations.Operation;
-import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
-import org.springframework.data.redis.core.RedisTemplate;
-import org.springframework.security.authentication.AuthenticationManager;
-import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;
-import java.util.Date;
-import java.util.concurrent.TimeUnit;
-
@Tag(name = "01.认证中心")
@RestController
@RequestMapping("/api/v1/auth")
@RequiredArgsConstructor
@Slf4j
public class AuthController {
- private final AuthenticationManager authenticationManager;
- private final JwtTokenManager jwtTokenManager;
- private final EasyCaptchaService easyCaptchaService;
- private final RedisTemplate redisTemplate;
+
+ private final AuthService authService;
@Operation(summary = "登录")
@PostMapping("/login")
@@ -43,46 +28,21 @@ public class AuthController {
@Parameter(description = "用户名", example = "admin") @RequestParam String username,
@Parameter(description = "密码", example = "123456") @RequestParam String password
) {
- UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
- username.toLowerCase().trim(),
- password
- );
- Authentication authentication = authenticationManager.authenticate(authenticationToken);
- // 生成token
- String accessToken = jwtTokenManager.createToken(authentication);
- LoginResult loginResult = LoginResult.builder()
- .tokenType("Bearer")
- .accessToken(accessToken)
- .build();
+ LoginResult loginResult = authService.login(username, password);
return Result.success(loginResult);
}
@Operation(summary = "注销", security = {@SecurityRequirement(name = SecurityConstants.TOKEN_KEY)})
@DeleteMapping("/logout")
- public Result logout(HttpServletRequest request) {
- String token = RequestUtils.resolveToken(request);
- if (StrUtil.isNotBlank(token)) {
- Claims claims = jwtTokenManager.getTokenClaims(token);
- String jti = claims.get("jti", String.class);
-
- Date expiration = claims.getExpiration();
- if (expiration != null) {
- // 有过期时间,在token有效时间内存入黑名单,超出时间移除黑名单节省内存占用
- long ttl = (expiration.getTime() - System.currentTimeMillis());
- redisTemplate.opsForValue().set(SecurityConstants.BLACK_TOKEN_CACHE_PREFIX + jti, null, ttl, TimeUnit.MILLISECONDS);
- } else {
- // 无过期时间,永久加入黑名单
- redisTemplate.opsForValue().set(SecurityConstants.BLACK_TOKEN_CACHE_PREFIX + jti, null);
- }
- }
- SecurityContextHolder.clearContext();
- return Result.success("注销成功");
+ public Result logout() {
+ authService.logout();
+ return Result.success();
}
@Operation(summary = "获取验证码")
@GetMapping("/captcha")
public Result getCaptcha() {
- CaptchaResult captcha = easyCaptchaService.getCaptcha();
+ CaptchaResult captcha = authService.getCaptcha();
return Result.success(captcha);
}
diff --git a/src/main/java/com/youlai/system/controller/SysUserController.java b/src/main/java/com/youlai/system/controller/SysUserController.java
index 66fda620..4b1a89bf 100644
--- a/src/main/java/com/youlai/system/controller/SysUserController.java
+++ b/src/main/java/com/youlai/system/controller/SysUserController.java
@@ -7,7 +7,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
import com.youlai.system.common.constant.ExcelConstants;
import com.youlai.system.common.result.PageResult;
import com.youlai.system.common.result.Result;
-import com.youlai.system.common.util.ExcelUtils;
+import com.youlai.system.util.ExcelUtils;
import com.youlai.system.common.annotation.PreventDuplicateSubmit;
import com.youlai.system.listener.easyexcel.UserImportListener;
import com.youlai.system.model.vo.UserImportVO;
@@ -128,8 +128,8 @@ public class SysUserController {
@Operation(summary = "获取当前登录用户信息", security = {@SecurityRequirement(name = "Authorization")})
@GetMapping("/me")
- public Result
+ * 模拟 张三 给 李四 发送消息场景
+ *
+ * @param principal 当前用户
+ * @param username 接收消息的用户
+ * @param message 消息内容
*/
- // 处理发送到"/app/sendToUser/{username}"的消息
@MessageMapping("/sendToUser/{username}")
- // 将消息处理器的返回值发送到指定用户
- @SendTo("/queue/user")
- public String sendToUser(@DestinationVariable("username") String username, String message) {
- // 处理消息
- return "Hello, " + username + ", your message is: " + message;
+ //@SendToUser(value = "/queue/greeting")
+ public void sendToUser(Principal principal, @DestinationVariable String username, String message) {
+ log.info("sender:{};receiver:{}", username, principal.getName());
+ messagingTemplate.convertAndSendToUser(username, "/queue/greeting", "Hello," + message);
+ /// return "Hello, " + message;
}
}
diff --git a/src/main/java/com/youlai/system/converter/UserConverter.java b/src/main/java/com/youlai/system/converter/UserConverter.java
index 5880fdcf..a094c8c5 100644
--- a/src/main/java/com/youlai/system/converter/UserConverter.java
+++ b/src/main/java/com/youlai/system/converter/UserConverter.java
@@ -39,7 +39,7 @@ public interface UserConverter {
@Mappings({
@Mapping(target = "userId", source = "id")
})
- UserInfoVO entity2UserInfoVo(SysUser entity);
+ UserInfoVO toUserInfoVo(SysUser entity);
SysUser importVo2Entity(UserImportVO vo);
diff --git a/src/main/java/com/youlai/system/common/exception/BusinessException.java b/src/main/java/com/youlai/system/exception/BusinessException.java
similarity index 93%
rename from src/main/java/com/youlai/system/common/exception/BusinessException.java
rename to src/main/java/com/youlai/system/exception/BusinessException.java
index a9f96205..9d7457e2 100644
--- a/src/main/java/com/youlai/system/common/exception/BusinessException.java
+++ b/src/main/java/com/youlai/system/exception/BusinessException.java
@@ -1,4 +1,4 @@
-package com.youlai.system.common.exception;
+package com.youlai.system.exception;
import com.youlai.system.common.result.IResultCode;
import lombok.Getter;
diff --git a/src/main/java/com/youlai/system/common/exception/GlobalExceptionHandler.java b/src/main/java/com/youlai/system/exception/GlobalExceptionHandler.java
similarity index 99%
rename from src/main/java/com/youlai/system/common/exception/GlobalExceptionHandler.java
rename to src/main/java/com/youlai/system/exception/GlobalExceptionHandler.java
index 5091b681..3d5fd03e 100644
--- a/src/main/java/com/youlai/system/common/exception/GlobalExceptionHandler.java
+++ b/src/main/java/com/youlai/system/exception/GlobalExceptionHandler.java
@@ -1,4 +1,4 @@
-package com.youlai.system.common.exception;
+package com.youlai.system.exception;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
diff --git a/src/main/java/com/youlai/system/filter/JwtAuthenticationFilter.java b/src/main/java/com/youlai/system/filter/JwtAuthenticationFilter.java
index 1ee24551..64364ae8 100644
--- a/src/main/java/com/youlai/system/filter/JwtAuthenticationFilter.java
+++ b/src/main/java/com/youlai/system/filter/JwtAuthenticationFilter.java
@@ -3,8 +3,8 @@ package com.youlai.system.filter;
import cn.hutool.core.util.StrUtil;
import com.youlai.system.common.constant.SecurityConstants;
import com.youlai.system.common.result.ResultCode;
-import com.youlai.system.common.util.RequestUtils;
-import com.youlai.system.common.util.ResponseUtils;
+import com.youlai.system.util.RequestUtils;
+import com.youlai.system.util.ResponseUtils;
import com.youlai.system.security.JwtTokenManager;
import io.jsonwebtoken.Claims;
import jakarta.servlet.FilterChain;
diff --git a/src/main/java/com/youlai/system/filter/VerifyCodeFilter.java b/src/main/java/com/youlai/system/filter/VerifyCodeFilter.java
index 3e6c5fb7..bcae036d 100644
--- a/src/main/java/com/youlai/system/filter/VerifyCodeFilter.java
+++ b/src/main/java/com/youlai/system/filter/VerifyCodeFilter.java
@@ -5,7 +5,7 @@ import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.youlai.system.common.constant.SecurityConstants;
import com.youlai.system.common.result.ResultCode;
-import com.youlai.system.common.util.ResponseUtils;
+import com.youlai.system.util.ResponseUtils;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
diff --git a/src/main/java/com/youlai/system/handler/mybatisplus/MyDataPermissionHandler.java b/src/main/java/com/youlai/system/handler/mybatisplus/MyDataPermissionHandler.java
index cb22cb64..aa713b79 100644
--- a/src/main/java/com/youlai/system/handler/mybatisplus/MyDataPermissionHandler.java
+++ b/src/main/java/com/youlai/system/handler/mybatisplus/MyDataPermissionHandler.java
@@ -7,7 +7,7 @@ import com.baomidou.mybatisplus.extension.plugins.handler.DataPermissionHandler;
import com.youlai.system.common.annotation.DataPermission;
import com.youlai.system.common.base.IBaseEnum;
import com.youlai.system.common.enums.DataScopeEnum;
-import com.youlai.system.common.util.SecurityUtils;
+import com.youlai.system.util.SecurityUtils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Expression;
diff --git a/src/main/java/com/youlai/system/interceptor/AuthChannelInterceptor.java b/src/main/java/com/youlai/system/interceptor/AuthChannelInterceptor.java
new file mode 100644
index 00000000..38bc7f7b
--- /dev/null
+++ b/src/main/java/com/youlai/system/interceptor/AuthChannelInterceptor.java
@@ -0,0 +1,63 @@
+package com.youlai.system.interceptor;
+
+import cn.hutool.core.util.StrUtil;
+import com.youlai.system.common.constant.SecurityConstants;
+import com.youlai.system.security.JwtTokenManager;
+import io.jsonwebtoken.Claims;
+import lombok.RequiredArgsConstructor;
+import org.springframework.messaging.Message;
+import org.springframework.messaging.MessageChannel;
+import org.springframework.messaging.simp.stomp.StompCommand;
+import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
+import org.springframework.messaging.support.ChannelInterceptor;
+import org.springframework.messaging.support.MessageHeaderAccessor;
+import org.springframework.stereotype.Component;
+
+import java.security.Principal;
+
+/**
+ * Websocket 连接认证拦截器
+ *
+ * @author haoxr
+ * @since 2.4.0
+ */
+@Component
+@RequiredArgsConstructor
+public class AuthChannelInterceptor implements ChannelInterceptor {
+
+ private final JwtTokenManager jwtTokenManager;
+
+ /**
+ * 连接前监听
+ *
+ * @param message
+ * @param channel
+ * @return
+ */
+ @Override
+ public Message> preSend(Message> message, MessageChannel channel) {
+ StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
+ assert accessor != null;
+
+ if (StompCommand.CONNECT.equals(accessor.getCommand())) {
+ // get token from header
+ String token = accessor.getFirstNativeHeader("Authorization");
+ // if token is not null
+ if (StrUtil.isNotBlank(token)) {
+
+ token = token.substring(SecurityConstants.TOKEN_PREFIX.length());
+ Claims claims = jwtTokenManager.parseAndValidateToken(token);
+
+ String username = claims.get("username", String.class);
+ // if the username is not null, assign it to the Principal.
+ if (StrUtil.isNotBlank(username)) {
+ Principal principal = () -> username;
+ accessor.setUser(principal);
+ return message;
+ }
+ }
+ }
+ return ChannelInterceptor.super.preSend(message, channel);
+ }
+
+}
diff --git a/src/main/java/com/youlai/system/model/vo/UserInfoVO.java b/src/main/java/com/youlai/system/model/vo/UserInfoVO.java
index 41ed88e1..9513e7a8 100644
--- a/src/main/java/com/youlai/system/model/vo/UserInfoVO.java
+++ b/src/main/java/com/youlai/system/model/vo/UserInfoVO.java
@@ -18,6 +18,9 @@ public class UserInfoVO {
@Schema(description="用户ID")
private Long userId;
+ @Schema(description="用户名")
+ private String username;
+
@Schema(description="用户昵称")
private String nickname;
diff --git a/src/main/java/com/youlai/system/security/JwtTokenManager.java b/src/main/java/com/youlai/system/security/JwtTokenManager.java
index cb9d24c2..20928dce 100644
--- a/src/main/java/com/youlai/system/security/JwtTokenManager.java
+++ b/src/main/java/com/youlai/system/security/JwtTokenManager.java
@@ -3,7 +3,7 @@ package com.youlai.system.security;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.IdUtil;
import com.youlai.system.common.constant.SecurityConstants;
-import com.youlai.system.security.userdetails.SysUserDetails;
+import com.youlai.system.security.model.SysUserDetails;
import io.jsonwebtoken.*;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.io.DecodingException;
diff --git a/src/main/java/com/youlai/system/security/captcha/EasyCaptchaProducer.java b/src/main/java/com/youlai/system/security/captcha/EasyCaptchaProducer.java
deleted file mode 100644
index 3b291ced..00000000
--- a/src/main/java/com/youlai/system/security/captcha/EasyCaptchaProducer.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package com.youlai.system.security.captcha;
-
-import com.wf.captcha.*;
-import com.wf.captcha.base.Captcha;
-import com.youlai.system.config.CaptchaConfig;
-import lombok.RequiredArgsConstructor;
-import org.springframework.stereotype.Component;
-
-import java.awt.*;
-
-/**
- * 验证码生成器
- *
- * @author haoxr
- * @since 2023/03/24
- */
-@Component
-@RequiredArgsConstructor
-public class EasyCaptchaProducer {
- private final CaptchaConfig captchaConfig;
-
- public Captcha getCaptcha() {
- Captcha captcha;
- int width = captchaConfig.getWidth();
- int height = captchaConfig.getHeight();
- int length = captchaConfig.getLength();
- String fontName = captchaConfig.getFontName();
-
- switch (captchaConfig.getType()) {
- case ARITHMETIC:
- captcha = new ArithmeticCaptcha(width, height);
- //固定设置为两位,图片为算数运算表达式
- captcha.setLen(2);
- break;
- case CHINESE:
- captcha = new ChineseCaptcha(width, height);
- captcha.setLen(length);
- break;
- case CHINESE_GIF:
- captcha = new ChineseGifCaptcha(width, height);
- captcha.setLen(length);
- break;
- case GIF:
- captcha = new GifCaptcha(width, height);//最后一位是位数
- captcha.setLen(length);
- break;
- case SPEC:
- captcha = new SpecCaptcha(width, height);
- captcha.setLen(length);
- break;
- default:
- throw new RuntimeException("验证码配置信息错误!正确配置查看 CaptchaTypeEnum ");
- }
- captcha.setFont(new Font(fontName, captchaConfig.getFontStyle(), captchaConfig.getFontSize()));
- return captcha;
-
- }
-
-
-}
diff --git a/src/main/java/com/youlai/system/security/captcha/EasyCaptchaService.java b/src/main/java/com/youlai/system/security/captcha/EasyCaptchaService.java
deleted file mode 100644
index 752e76f0..00000000
--- a/src/main/java/com/youlai/system/security/captcha/EasyCaptchaService.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package com.youlai.system.security.captcha;
-
-import cn.hutool.core.util.IdUtil;
-import com.wf.captcha.base.Captcha;
-import com.youlai.system.common.constant.SecurityConstants;
-import com.youlai.system.config.CaptchaConfig;
-import com.youlai.system.model.dto.CaptchaResult;
-import lombok.RequiredArgsConstructor;
-import org.springframework.data.redis.core.RedisTemplate;
-import org.springframework.stereotype.Component;
-
-import java.util.concurrent.TimeUnit;
-
-/**
- * EasyCaptcha 业务类
- *
- * @author haoxr
- * @since 2023/03/24
- */
-@Component
-@RequiredArgsConstructor
-public class EasyCaptchaService {
-
- private final EasyCaptchaProducer easyCaptchaProducer;
-
- private final RedisTemplate redisTemplate;
-
- private final CaptchaConfig captchaConfig;
-
- /**
- * 获取验证码
- *
- * @return
- */
- public CaptchaResult getCaptcha() {
- // 获取验证码
- Captcha captcha = easyCaptchaProducer.getCaptcha();
- String captchaText = captcha.text(); // 验证码文本
- String captchaBase64 = captcha.toBase64(); // 验证码图片Base64字符串
-
- // 验证码文本缓存至Redis,用于登录校验
- String verifyCodeKey = IdUtil.fastSimpleUUID();
- redisTemplate.opsForValue().set(SecurityConstants.VERIFY_CODE_CACHE_PREFIX + verifyCodeKey, captchaText,
- captchaConfig.getTtl(), TimeUnit.SECONDS);
-
- return CaptchaResult.builder()
- .verifyCodeKey(verifyCodeKey)
- .verifyCodeBase64(captchaBase64)
- .build();
- }
-
-}
diff --git a/src/main/java/com/youlai/system/security/exception/MyAccessDeniedHandler.java b/src/main/java/com/youlai/system/security/exception/MyAccessDeniedHandler.java
index 83515d17..78b0fbc8 100644
--- a/src/main/java/com/youlai/system/security/exception/MyAccessDeniedHandler.java
+++ b/src/main/java/com/youlai/system/security/exception/MyAccessDeniedHandler.java
@@ -1,7 +1,7 @@
package com.youlai.system.security.exception;
import com.youlai.system.common.result.ResultCode;
-import com.youlai.system.common.util.ResponseUtils;
+import com.youlai.system.util.ResponseUtils;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
diff --git a/src/main/java/com/youlai/system/security/exception/MyAuthenticationEntryPoint.java b/src/main/java/com/youlai/system/security/exception/MyAuthenticationEntryPoint.java
index 8c007312..077fe786 100644
--- a/src/main/java/com/youlai/system/security/exception/MyAuthenticationEntryPoint.java
+++ b/src/main/java/com/youlai/system/security/exception/MyAuthenticationEntryPoint.java
@@ -1,7 +1,7 @@
package com.youlai.system.security.exception;
import com.youlai.system.common.result.ResultCode;
-import com.youlai.system.common.util.ResponseUtils;
+import com.youlai.system.util.ResponseUtils;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
diff --git a/src/main/java/com/youlai/system/security/userdetails/SysUserDetails.java b/src/main/java/com/youlai/system/security/model/SysUserDetails.java
similarity index 98%
rename from src/main/java/com/youlai/system/security/userdetails/SysUserDetails.java
rename to src/main/java/com/youlai/system/security/model/SysUserDetails.java
index bc32ccdc..35b24d21 100644
--- a/src/main/java/com/youlai/system/security/userdetails/SysUserDetails.java
+++ b/src/main/java/com/youlai/system/security/model/SysUserDetails.java
@@ -1,4 +1,4 @@
-package com.youlai.system.security.userdetails;
+package com.youlai.system.security.model;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
diff --git a/src/main/java/com/youlai/system/security/service/PermissionService.java b/src/main/java/com/youlai/system/security/service/PermissionService.java
index 072119b7..f614c42e 100644
--- a/src/main/java/com/youlai/system/security/service/PermissionService.java
+++ b/src/main/java/com/youlai/system/security/service/PermissionService.java
@@ -3,12 +3,11 @@ package com.youlai.system.security.service;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.youlai.system.common.constant.SecurityConstants;
-import com.youlai.system.common.util.SecurityUtils;
+import com.youlai.system.util.SecurityUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
-import org.springframework.stereotype.Service;
import org.springframework.util.PatternMatchUtils;
import java.util.Set;
diff --git a/src/main/java/com/youlai/system/security/userdetails/SysUserDetailsService.java b/src/main/java/com/youlai/system/security/service/SysUserDetailsService.java
similarity index 90%
rename from src/main/java/com/youlai/system/security/userdetails/SysUserDetailsService.java
rename to src/main/java/com/youlai/system/security/service/SysUserDetailsService.java
index fe735a2f..17a04549 100644
--- a/src/main/java/com/youlai/system/security/userdetails/SysUserDetailsService.java
+++ b/src/main/java/com/youlai/system/security/service/SysUserDetailsService.java
@@ -1,6 +1,7 @@
-package com.youlai.system.security.userdetails;
+package com.youlai.system.security.service;
import com.youlai.system.model.dto.UserAuthInfo;
+import com.youlai.system.security.model.SysUserDetails;
import com.youlai.system.service.SysUserService;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.userdetails.UserDetails;
diff --git a/src/main/java/com/youlai/system/service/AuthService.java b/src/main/java/com/youlai/system/service/AuthService.java
new file mode 100644
index 00000000..40c6c4bb
--- /dev/null
+++ b/src/main/java/com/youlai/system/service/AuthService.java
@@ -0,0 +1,34 @@
+package com.youlai.system.service;
+
+import com.youlai.system.model.dto.CaptchaResult;
+import com.youlai.system.model.dto.LoginResult;
+
+/**
+ * 认证服务接口
+ *
+ * @author haoxr
+ * @since 2.4.0
+ */
+public interface AuthService {
+
+ /**
+ * 登录
+ *
+ * @param username 用户名
+ * @param password 密码
+ * @return 登录结果
+ */
+ LoginResult login(String username, String password);
+
+ /**
+ * 登出
+ */
+ void logout();
+
+ /**
+ * 获取验证码
+ *
+ * @return 验证码
+ */
+ CaptchaResult getCaptcha();
+}
diff --git a/src/main/java/com/youlai/system/service/SysUserService.java b/src/main/java/com/youlai/system/service/SysUserService.java
index 7dca75f0..cbf905bd 100644
--- a/src/main/java/com/youlai/system/service/SysUserService.java
+++ b/src/main/java/com/youlai/system/service/SysUserService.java
@@ -98,5 +98,5 @@ public interface SysUserService extends IService