Эх сурвалжийг харах

1. 增加前台用户的 token 刷新
2. 增加前台用户的 logout 退出

YunaiV 3 жил өмнө
parent
commit
024d44cea1
13 өөрчлөгдсөн 166 нэмэгдсэн , 29 устгасан
  1. 1 4
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/enums/SysErrorCodeConstants.java
  2. 5 5
      yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/impl/SysAuthServiceImpl.java
  3. 11 0
      yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/service/user/MbrUserService.java
  4. 32 0
      yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/service/user/impl/MbrUserServiceImpl.java
  5. 15 1
      yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/SysAuthController.http
  6. 10 8
      yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/SysAuthController.java
  7. 1 1
      yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthResetPasswordReqVO.java
  8. 1 1
      yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/SysAuthLoginRespVO.java
  9. 1 1
      yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/SysAuthSendSmsReqVO.java
  10. 1 6
      yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/SysAuthSmsLoginReqVO.java
  11. 1 0
      yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/enums/SysErrorCodeConstants.java
  12. 11 0
      yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/auth/SysAuthService.java
  13. 76 2
      yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/auth/impl/SysAuthServiceImpl.java

+ 1 - 4
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/enums/SysErrorCodeConstants.java

@@ -16,10 +16,7 @@ public interface SysErrorCodeConstants {
     ErrorCode AUTH_LOGIN_CAPTCHA_NOT_FOUND = new ErrorCode(1002000003, "验证码不存在");
     ErrorCode AUTH_LOGIN_CAPTCHA_CODE_ERROR = new ErrorCode(1002000004, "验证码不正确");
     ErrorCode AUTH_THIRD_LOGIN_NOT_BIND = new ErrorCode(1002000005, "未绑定账号,需要进行绑定");
-
-    // ========== TOKEN 模块 1002001000 ==========
-    ErrorCode TOKEN_EXPIRED = new ErrorCode(1002001000, "Token 已经过期");
-    ErrorCode TOKEN_PARSE_FAIL = new ErrorCode(1002001001, "Token 解析失败");
+    ErrorCode AUTH_TOKEN_EXPIRED = new ErrorCode(1002000006, "Token 已经过期");
 
     // ========== 菜单模块 1002002000 ==========
     ErrorCode MENU_NAME_DUPLICATE = new ErrorCode(1002002000, "已经存在该名字的菜单");

+ 5 - 5
yudao-admin-server/src/main/java/cn/iocoder/yudao/adminserver/modules/system/service/auth/impl/SysAuthServiceImpl.java

@@ -255,15 +255,15 @@ public class SysAuthServiceImpl implements SysAuthService {
         }
         // 删除 session
         userSessionCoreService.deleteUserSession(token);
-        // 记录登出日子和
-        this.createLogoutLog(loginUser.getUsername());
+        // 记录登出日
+        this.createLogoutLog(loginUser.getId(), loginUser.getUsername());
     }
 
-    private void createLogoutLog(String username) {
-        // TODO 芋艿:这里未设置 userId
+    private void createLogoutLog(Long userId, String username) {
         SysLoginLogCreateReqDTO reqDTO = new SysLoginLogCreateReqDTO();
         reqDTO.setLogType(SysLoginLogTypeEnum.LOGOUT_SELF.getType());
         reqDTO.setTraceId(TracerUtils.getTraceId());
+        reqDTO.setUserId(userId);
         reqDTO.setUserType(UserTypeEnum.ADMIN.getValue());
         reqDTO.setUsername(username);
         reqDTO.setUserAgent(ServletUtils.getUserAgent());
@@ -294,7 +294,7 @@ public class SysAuthServiceImpl implements SysAuthService {
         // 重新加载 SysUserDO 信息
         SysUserDO user = userService.getUser(loginUser.getId());
         if (user == null || CommonStatusEnum.DISABLE.getStatus().equals(user.getStatus())) {
-            throw exception(TOKEN_EXPIRED); // 校验 token 时,用户被禁用的情况下,也认为 token 过期,方便前端跳转到登录界面
+            throw exception(AUTH_TOKEN_EXPIRED); // 校验 token 时,用户被禁用的情况下,也认为 token 过期,方便前端跳转到登录界面
         }
 
         // 刷新 LoginUser 缓存

+ 11 - 0
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/service/user/MbrUserService.java

@@ -1,5 +1,6 @@
 package cn.iocoder.yudao.userserver.modules.member.service.user;
 
+import cn.iocoder.yudao.framework.common.validation.Mobile;
 import cn.iocoder.yudao.userserver.modules.member.dal.dataobject.user.MbrUserDO;
 
 /**
@@ -17,6 +18,16 @@ public interface MbrUserService {
      */
     MbrUserDO getUserByMobile(String mobile);
 
+    /**
+     * 基于手机号创建用户。
+     * 如果用户已经存在,则直接进行返回
+     *
+     * @param mobile 手机号
+     * @param registerIp 注册 IP
+     * @return 用户对象
+     */
+    MbrUserDO createUserIfAbsent(@Mobile String mobile, String registerIp);
+
     /**
      * 更新用户的最后登陆信息
      *

+ 32 - 0
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/member/service/user/impl/MbrUserServiceImpl.java

@@ -1,9 +1,14 @@
 package cn.iocoder.yudao.userserver.modules.member.service.user.impl;
 
+import cn.hutool.core.util.IdUtil;
+import cn.hutool.core.util.RandomUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.userserver.modules.member.dal.dataobject.user.MbrUserDO;
 import cn.iocoder.yudao.userserver.modules.member.dal.mysql.user.MbrUserMapper;
 import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.Resource;
@@ -23,11 +28,38 @@ public class MbrUserServiceImpl implements MbrUserService {
     @Resource
     private MbrUserMapper userMapper;
 
+    @Resource
+    private PasswordEncoder passwordEncoder;
+
     @Override
     public MbrUserDO getUserByMobile(String mobile) {
         return userMapper.selectByMobile(mobile);
     }
 
+    @Override
+    public MbrUserDO createUserIfAbsent(String mobile, String registerIp) {
+        // 用户已经存在
+        MbrUserDO user = userMapper.selectByMobile(mobile);
+        if (user != null) {
+            return user;
+        }
+        // 用户不存在,则进行创建
+        return this.createUser(mobile, registerIp);
+    }
+
+    private MbrUserDO createUser(String mobile, String registerIp) {
+        // 生成密码
+        String password = IdUtil.fastSimpleUUID();
+        // 插入用户
+        MbrUserDO user = new MbrUserDO();
+        user.setMobile(mobile);
+        user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启
+        user.setPassword(passwordEncoder.encode(password)); // 加密密码
+        user.setRegisterIp(registerIp);
+        userMapper.insert(user);
+        return user;
+    }
+
     @Override
     public void updateUserLogin(Long id, String loginIp) {
         userMapper.updateById(new MbrUserDO().setId(id).setLoginIp(loginIp).setLoginDate(new Date()));

+ 15 - 1
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/SysAuthController.http

@@ -12,6 +12,20 @@ POST {{userServerUrl}}/send-sms-code
 Content-Type: application/json
 
 {
-  "mobile": "15601691300",
+  "mobile": "15601691301",
   "scene": 1
 }
+
+### 请求 /sms-login 接口 => 成功
+POST {{userServerUrl}}/sms-login
+Content-Type: application/json
+
+{
+  "mobile": "15601691301",
+  "code": 9999
+}
+
+### 请求 /logout 接口 => 成功
+POST {{userServerUrl}}/logout
+Content-Type: application/json
+Authorization: Bearer c1b76bdaf2c146c581caa4d7fd81ee66

+ 10 - 8
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/SysAuthController.java

@@ -33,28 +33,30 @@ public class SysAuthController {
 
     @PostMapping("/login")
     @ApiOperation("使用手机 + 密码登录")
-    public CommonResult<MbrAuthLoginRespVO> login(@RequestBody @Valid SysAuthLoginReqVO reqVO) {
+    public CommonResult<SysAuthLoginRespVO> login(@RequestBody @Valid SysAuthLoginReqVO reqVO) {
         String token = authService.login(reqVO, getClientIP(), getUserAgent());
         // 返回结果
-        return success(MbrAuthLoginRespVO.builder().token(token).build());
+        return success(SysAuthLoginRespVO.builder().token(token).build());
     }
 
     @PostMapping("/sms-login")
     @ApiOperation("使用手机 + 验证码登录")
-    public CommonResult<MbrAuthLoginRespVO> smsLogin(@RequestBody @Valid SysAuthLoginReqVO reqVO) {
-        return null;
+    public CommonResult<SysAuthLoginRespVO> smsLogin(@RequestBody @Valid SysAuthSmsLoginReqVO reqVO) {
+        String token = authService.smsLogin(reqVO, getClientIP(), getUserAgent());
+        // 返回结果
+        return success(SysAuthLoginRespVO.builder().token(token).build());
     }
 
     @PostMapping("/send-sms-code")
     @ApiOperation("发送手机验证码")
-    public CommonResult<Boolean> sendSmsCode(@RequestBody @Valid MbrAuthSendSmsReqVO reqVO) {
+    public CommonResult<Boolean> sendSmsCode(@RequestBody @Valid SysAuthSendSmsReqVO reqVO) {
         smsCodeService.sendSmsCode(reqVO.getMobile(), reqVO.getScene(), getClientIP());
         return success(true);
     }
 
     @PostMapping("/reset-password")
     @ApiOperation(value = "重置密码", notes = "用户忘记密码时使用")
-    public CommonResult<Boolean> resetPassword(@RequestBody @Valid SysAuthResetPasswordReqVO reqVO) {
+    public CommonResult<Boolean> resetPassword(@RequestBody @Valid MbrAuthResetPasswordReqVO reqVO) {
         return null;
     }
 
@@ -74,7 +76,7 @@ public class SysAuthController {
 
     @PostMapping("/social-login")
     @ApiOperation("社交登录,使用 code 授权码")
-        public CommonResult<MbrAuthLoginRespVO> socialLogin(@RequestBody @Valid MbrAuthSocialLoginReqVO reqVO) {
+        public CommonResult<SysAuthLoginRespVO> socialLogin(@RequestBody @Valid MbrAuthSocialLoginReqVO reqVO) {
 //        String token = authService.socialLogin(reqVO, getClientIP(), getUserAgent());
 //        // 返回结果
 //        return success(MbrAuthLoginRespVO.builder().token(token).build());
@@ -83,7 +85,7 @@ public class SysAuthController {
 
     @PostMapping("/social-login2")
     @ApiOperation("社交登录,使用 code 授权码 + 账号密码")
-    public CommonResult<MbrAuthLoginRespVO> socialLogin2(@RequestBody @Valid MbrAuthSocialLogin2ReqVO reqVO) {
+    public CommonResult<SysAuthLoginRespVO> socialLogin2(@RequestBody @Valid MbrAuthSocialLogin2ReqVO reqVO) {
 //        String token = authService.socialLogin2(reqVO, getClientIP(), getUserAgent());
 //        // 返回结果
 //        return success(MbrAuthLoginRespVO.builder().token(token).build());

+ 1 - 1
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/SysAuthResetPasswordReqVO.java → yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthResetPasswordReqVO.java

@@ -16,7 +16,7 @@ import javax.validation.constraints.Pattern;
 @NoArgsConstructor
 @AllArgsConstructor
 @Builder
-public class SysAuthResetPasswordReqVO {
+public class MbrAuthResetPasswordReqVO {
 
     @ApiModelProperty(value = "新密码", required = true, example = "buzhidao")
     @NotEmpty(message = "新密码不能为空")

+ 1 - 1
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthLoginRespVO.java → yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/SysAuthLoginRespVO.java

@@ -12,7 +12,7 @@ import lombok.NoArgsConstructor;
 @NoArgsConstructor
 @AllArgsConstructor
 @Builder
-public class MbrAuthLoginRespVO {
+public class SysAuthLoginRespVO {
 
     @ApiModelProperty(value = "token", required = true, example = "yudaoyuanma")
     private String token;

+ 1 - 1
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthSendSmsReqVO.java → yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/SysAuthSendSmsReqVO.java

@@ -13,7 +13,7 @@ import javax.validation.constraints.NotNull;
 @ApiModel("发送手机验证码 Response VO")
 @Data
 @Accessors(chain = true)
-public class MbrAuthSendSmsReqVO {
+public class SysAuthSendSmsReqVO {
 
     @ApiModelProperty(value = "手机号", example = "15601691234")
     @Mobile

+ 1 - 6
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/MbrAuthSmsLoginReqVO.java → yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/controller/auth/vo/SysAuthSmsLoginReqVO.java

@@ -17,18 +17,13 @@ import javax.validation.constraints.Pattern;
 @NoArgsConstructor
 @AllArgsConstructor
 @Builder
-public class MbrAuthSmsLoginReqVO {
+public class SysAuthSmsLoginReqVO {
 
     @ApiModelProperty(value = "手机号", required = true, example = "15601691300")
     @NotEmpty(message = "手机号不能为空")
     @Mobile
     private String mobile;
 
-    @ApiModelProperty(value = "密码", required = true, example = "buzhidao")
-    @NotEmpty(message = "密码不能为空")
-    @Length(min = 4, max = 16, message = "密码长度为 4-16 位")
-    private String password;
-
     @ApiModelProperty(value = "手机验证码", required = true, example = "1024")
     @NotEmpty(message = "手机验证码不能为空")
     @Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位")

+ 1 - 0
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/enums/SysErrorCodeConstants.java

@@ -13,6 +13,7 @@ public interface SysErrorCodeConstants {
     ErrorCode AUTH_LOGIN_BAD_CREDENTIALS = new ErrorCode(1005000000, "登录失败,账号密码不正确");
     ErrorCode AUTH_LOGIN_USER_DISABLED = new ErrorCode(1005000001, "登录失败,账号被禁用");
     ErrorCode AUTH_LOGIN_FAIL_UNKNOWN = new ErrorCode(1005000002, "登录失败"); // 登录失败的兜底,未知原因
+    ErrorCode AUTH_TOKEN_EXPIRED = new ErrorCode(1005000003, "Token 已经过期");
 
     // ========== SMS CODE 模块 1005001000 ==========
     ErrorCode USER_SMS_CODE_NOT_FOUND = new ErrorCode(1005001000, "验证码不存在");

+ 11 - 0
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/auth/SysAuthService.java

@@ -2,6 +2,7 @@ package cn.iocoder.yudao.userserver.modules.system.service.auth;
 
 import cn.iocoder.yudao.framework.security.core.service.SecurityAuthFrameworkService;
 import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.SysAuthLoginReqVO;
+import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.SysAuthSmsLoginReqVO;
 
 import javax.validation.Valid;
 
@@ -24,4 +25,14 @@ public interface SysAuthService extends SecurityAuthFrameworkService {
      */
     String login(@Valid SysAuthLoginReqVO reqVO, String userIp, String userAgent);
 
+    /**
+     * 手机 + 验证码登陆
+     *
+     * @param reqVO 登陆信息
+     * @param userIp 用户 IP
+     * @param userAgent 用户 UA
+     * @return 身份令牌,使用 JWT 方式
+     */
+    String smsLogin(@Valid SysAuthSmsLoginReqVO reqVO, String userIp, String userAgent);
+
 }

+ 76 - 2
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/system/service/auth/impl/SysAuthServiceImpl.java

@@ -1,18 +1,24 @@
 package cn.iocoder.yudao.userserver.modules.system.service.auth.impl;
 
+import cn.hutool.core.lang.Assert;
 import cn.iocoder.yudao.coreservice.modules.system.service.auth.SysUserSessionCoreService;
 import cn.iocoder.yudao.coreservice.modules.system.service.logger.SysLoginLogCoreService;
 import cn.iocoder.yudao.coreservice.modules.system.service.logger.dto.SysLoginLogCreateReqDTO;
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
+import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
 import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
 import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
 import cn.iocoder.yudao.framework.security.core.LoginUser;
 import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.SysAuthLoginReqVO;
+import cn.iocoder.yudao.userserver.modules.system.controller.auth.vo.SysAuthSmsLoginReqVO;
 import cn.iocoder.yudao.userserver.modules.system.convert.auth.SysAuthConvert;
 import cn.iocoder.yudao.userserver.modules.member.dal.dataobject.user.MbrUserDO;
+import cn.iocoder.yudao.userserver.modules.system.enums.sms.SysSmsSceneEnum;
 import cn.iocoder.yudao.userserver.modules.system.service.auth.SysAuthService;
 import cn.iocoder.yudao.userserver.modules.member.service.user.MbrUserService;
 import cn.iocoder.yudao.coreservice.modules.system.enums.logger.SysLoginLogTypeEnum;
 import cn.iocoder.yudao.coreservice.modules.system.enums.logger.SysLoginResultEnum;
+import cn.iocoder.yudao.userserver.modules.system.service.sms.SysSmsCodeService;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.security.authentication.AuthenticationManager;
@@ -24,7 +30,7 @@ import org.springframework.security.core.AuthenticationException;
 import org.springframework.security.core.userdetails.UserDetails;
 import org.springframework.security.core.userdetails.UsernameNotFoundException;
 import org.springframework.stereotype.Service;
-import org.springframework.util.Assert;
+import org.springframework.transaction.annotation.Transactional;
 
 import javax.annotation.Resource;
 import java.util.Objects;
@@ -48,6 +54,8 @@ public class SysAuthServiceImpl implements SysAuthService {
     @Resource
     private MbrUserService userService;
     @Resource
+    private SysSmsCodeService smsCodeService;
+    @Resource
     private SysLoginLogCoreService loginLogCoreService;
     @Resource
     private SysUserSessionCoreService userSessionCoreService;
@@ -72,6 +80,25 @@ public class SysAuthServiceImpl implements SysAuthService {
         return userSessionCoreService.createUserSession(loginUser, userIp, userAgent);
     }
 
+    @Override
+    @Transactional
+    public String smsLogin(SysAuthSmsLoginReqVO reqVO, String userIp, String userAgent) {
+        // 校验验证码
+        smsCodeService.useSmsCode(reqVO.getMobile(), SysSmsSceneEnum.LOGIN_BY_SMS.getScene(),
+                reqVO.getCode(), userIp);
+
+        // 获得获得注册用户
+        MbrUserDO user = userService.createUserIfAbsent(reqVO.getMobile(), userIp);
+        Assert.notNull(user, "获取用户失败,结果为空");
+
+        // 执行登陆
+        this.createLoginLog(user.getMobile(), SysLoginLogTypeEnum.LOGIN_SMS, SysLoginResultEnum.SUCCESS);
+        LoginUser loginUser = SysAuthConvert.INSTANCE.convert(user);
+
+        // 缓存登录用户到 Redis 中,返回 sessionId 编号
+        return userSessionCoreService.createUserSession(loginUser, userIp, userAgent);
+    }
+
     private LoginUser login0(String username, String password) {
         final SysLoginLogTypeEnum logTypeEnum = SysLoginLogTypeEnum.LOGIN_USERNAME;
         // 用户验证
@@ -120,7 +147,31 @@ public class SysAuthServiceImpl implements SysAuthService {
 
     @Override
     public LoginUser verifyTokenAndRefresh(String token) {
-        return null;
+        // 获得 LoginUser
+        LoginUser loginUser = userSessionCoreService.getLoginUser(token);
+        if (loginUser == null) {
+            return null;
+        }
+        // 刷新 LoginUser 缓存
+        this.refreshLoginUserCache(token, loginUser);
+        return loginUser;
+    }
+
+    private void refreshLoginUserCache(String token, LoginUser loginUser) {
+        // 每 1/3 的 Session 超时时间,刷新 LoginUser 缓存
+        if (System.currentTimeMillis() - loginUser.getUpdateTime().getTime() <
+                userSessionCoreService.getSessionTimeoutMillis() / 3) {
+            return;
+        }
+
+        // 重新加载 MbrUserDO 信息
+        MbrUserDO user = userService.getUser(loginUser.getId());
+        if (user == null || CommonStatusEnum.DISABLE.getStatus().equals(user.getStatus())) {
+            throw exception(AUTH_TOKEN_EXPIRED); // 校验 token 时,用户被禁用的情况下,也认为 token 过期,方便前端跳转到登录界面
+        }
+
+        // 刷新 LoginUser 缓存
+        userSessionCoreService.refreshUserSession(token, loginUser);
     }
 
     @Override
@@ -130,6 +181,8 @@ public class SysAuthServiceImpl implements SysAuthService {
         if (user == null) {
             throw new UsernameNotFoundException(String.valueOf(userId));
         }
+
+        // 执行登陆
         this.createLoginLog(user.getMobile(), SysLoginLogTypeEnum.LOGIN_MOCK, SysLoginResultEnum.SUCCESS);
 
         // 创建 LoginUser 对象
@@ -138,7 +191,28 @@ public class SysAuthServiceImpl implements SysAuthService {
 
     @Override
     public void logout(String token) {
+        // 查询用户信息
+        LoginUser loginUser = userSessionCoreService.getLoginUser(token);
+        if (loginUser == null) {
+            return;
+        }
+        // 删除 session
+        userSessionCoreService.deleteUserSession(token);
+        // 记录登出日志
+        this.createLogoutLog(loginUser.getId(), loginUser.getUsername());
+    }
 
+    private void createLogoutLog(Long userId, String username) {
+        SysLoginLogCreateReqDTO reqDTO = new SysLoginLogCreateReqDTO();
+        reqDTO.setLogType(SysLoginLogTypeEnum.LOGOUT_SELF.getType());
+        reqDTO.setTraceId(TracerUtils.getTraceId());
+        reqDTO.setUserId(userId);
+        reqDTO.setUserType(UserTypeEnum.MEMBER.getValue());
+        reqDTO.setUsername(username);
+        reqDTO.setUserAgent(ServletUtils.getUserAgent());
+        reqDTO.setUserIp(ServletUtils.getClientIP());
+        reqDTO.setResult(SysLoginResultEnum.SUCCESS.getResult());
+        loginLogCoreService.createLoginLog(reqDTO);
     }
 
 }