refactor: 认证和短信发送重构
This commit is contained in:
@@ -31,8 +31,13 @@ public interface RedisConstants {
|
||||
/**
|
||||
* 手机验证码缓存前缀
|
||||
*/
|
||||
String SMS_LOGIN_VERIFY_CODE_PREFIX = "sms_login:mobile:";
|
||||
|
||||
String MOBILE_VERIFICATION_CODE_PREFIX = "VERIFICATION_CODE:MOBILE:";
|
||||
/**
|
||||
* 重置密码验证码缓存前缀
|
||||
*/
|
||||
|
||||
String SMS_RESET_PASSWORD_VERIFY_CODE_PREFIX = "sms_reset_password:mobile:";
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,7 +11,7 @@ import com.youlai.boot.core.security.extension.WechatAuthenticationProvider;
|
||||
import com.youlai.boot.core.security.filter.CaptchaValidationFilter;
|
||||
import com.youlai.boot.core.security.filter.JwtAuthenticationFilter;
|
||||
import com.youlai.boot.core.security.service.SysUserDetailsService;
|
||||
import com.youlai.boot.shared.auth.service.impl.JwtTokenService;
|
||||
import com.youlai.boot.core.security.manager.JwtTokenManager;
|
||||
import com.youlai.boot.system.service.ConfigService;
|
||||
import com.youlai.boot.system.service.UserService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -47,7 +47,7 @@ public class SecurityConfig {
|
||||
private final RedisTemplate<String, Object> redisTemplate;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
|
||||
private final JwtTokenService jwtTokenService;
|
||||
private final JwtTokenManager jwtTokenService;
|
||||
private final WxMaService wxMaService;
|
||||
private final UserService userService;
|
||||
private final SysUserDetailsService userDetailsService;
|
||||
|
||||
@@ -43,8 +43,8 @@ public class AliyunSmsProperties {
|
||||
private String signName;
|
||||
|
||||
/**
|
||||
* 模板编码
|
||||
* 短信模板集合
|
||||
*/
|
||||
private Map<String, String> templateCodes;
|
||||
private Map<String, String> templates;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.youlai.boot.core.security.extension.sms;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.youlai.boot.common.constant.RedisConstants;
|
||||
import com.youlai.boot.core.security.extension.WechatAuthenticationToken;
|
||||
import com.youlai.boot.core.security.model.SysUserDetails;
|
||||
import com.youlai.boot.system.model.dto.UserAuthInfo;
|
||||
import com.youlai.boot.system.service.UserService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.CredentialsExpiredException;
|
||||
import org.springframework.security.authentication.DisabledException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
|
||||
|
||||
/**
|
||||
* 短信验证码认证 Provider
|
||||
*
|
||||
* @author Ray.Hao
|
||||
* @since 2.17.0
|
||||
*/
|
||||
@Slf4j
|
||||
public class SmsAuthenticationProvider implements AuthenticationProvider {
|
||||
|
||||
private final UserService userService;
|
||||
|
||||
private final StringRedisTemplate redisTemplate;
|
||||
|
||||
|
||||
public SmsAuthenticationProvider(UserService userService, StringRedisTemplate redisTemplate) {
|
||||
this.userService = userService;
|
||||
this.redisTemplate = redisTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* 短信验证码认证逻辑,参考 Spring Security 认证密码校验流程
|
||||
*
|
||||
* @param authentication 认证对象
|
||||
* @return 认证后的 Authentication 对象
|
||||
* @throws AuthenticationException 认证异常
|
||||
* @see org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider#authenticate(Authentication)
|
||||
*/
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
String mobile = (String) authentication.getPrincipal();
|
||||
String verifyCode = (String) authentication.getCredentials();
|
||||
|
||||
// 根据手机号获取用户信息
|
||||
UserAuthInfo userAuthInfo = userService.getUserAuthInfoByMobile(mobile);
|
||||
|
||||
// 检查用户状态是否有效
|
||||
if (ObjectUtil.notEqual(userAuthInfo.getStatus(), 1)) {
|
||||
throw new DisabledException("用户已被禁用");
|
||||
}
|
||||
|
||||
// 校验发送短信验证码的手机号是否与当前登录用户一致
|
||||
|
||||
String cachedVerifyCode= redisTemplate.opsForValue().get(RedisConstants.SMS_LOGIN_VERIFY_CODE_PREFIX + mobile);
|
||||
|
||||
if ( !StrUtil.equals(verifyCode, cachedVerifyCode)) {
|
||||
throw new CredentialsExpiredException("验证码错误");
|
||||
}
|
||||
|
||||
// 构建认证后的用户详情信息
|
||||
SysUserDetails userDetails = new SysUserDetails(userAuthInfo);
|
||||
|
||||
// 创建已认证的 WeChatAuthenticationToken
|
||||
return SmsAuthenticationToken.authenticated(
|
||||
userDetails,
|
||||
userDetails.getAuthorities()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> authentication) {
|
||||
return WechatAuthenticationToken.class.isAssignableFrom(authentication);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.youlai.boot.core.security.extension.sms;
|
||||
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* 短信验证码认证 Token
|
||||
*
|
||||
* @author Ray.Hao
|
||||
* @since 2.20.0
|
||||
*/
|
||||
public class SmsAuthenticationToken extends AbstractAuthenticationToken {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 621L;
|
||||
private final Object principal;
|
||||
private Object credentials;
|
||||
|
||||
/**
|
||||
* 短信验证码认证 Token (未认证)
|
||||
*
|
||||
* @param principal 微信用户信息
|
||||
*/
|
||||
public SmsAuthenticationToken(Object principal) {
|
||||
// 没有授权信息时,设置为 null
|
||||
super(null);
|
||||
this.principal = principal;
|
||||
// 默认未认证
|
||||
this.setAuthenticated(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 短信验证码认证 Token (已认证)
|
||||
*
|
||||
* @param principal 用户信息
|
||||
* @param authorities 授权信息
|
||||
*/
|
||||
public SmsAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {
|
||||
super(authorities);
|
||||
this.principal = principal;
|
||||
// 认证通过
|
||||
super.setAuthenticated(true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 认证通过
|
||||
*
|
||||
* @param principal 用户信息
|
||||
* @param authorities 授权信息
|
||||
* @return
|
||||
*/
|
||||
public static SmsAuthenticationToken authenticated(Object principal, Collection<? extends GrantedAuthority> authorities) {
|
||||
return new SmsAuthenticationToken(principal, authorities);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCredentials() {
|
||||
return this.credentials ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrincipal() {
|
||||
return this.principal;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import cn.hutool.core.util.StrUtil;
|
||||
import com.youlai.boot.common.constant.SecurityConstants;
|
||||
import com.youlai.boot.common.result.ResultCode;
|
||||
import com.youlai.boot.common.util.ResponseUtils;
|
||||
import com.youlai.boot.shared.auth.service.impl.JwtTokenService;
|
||||
import com.youlai.boot.core.security.manager.JwtTokenManager;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
@@ -24,10 +24,10 @@ import java.io.IOException;
|
||||
*/
|
||||
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||
|
||||
private final JwtTokenService jwtTokenService;
|
||||
private final JwtTokenManager jwtTokenService;
|
||||
|
||||
|
||||
public JwtAuthenticationFilter(JwtTokenService jwtTokenService) {
|
||||
public JwtAuthenticationFilter(JwtTokenManager jwtTokenService) {
|
||||
this.jwtTokenService = jwtTokenService;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.youlai.boot.shared.auth.service.impl;
|
||||
package com.youlai.boot.core.security.manager;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
@@ -13,8 +13,7 @@ import com.youlai.boot.common.exception.BusinessException;
|
||||
import com.youlai.boot.common.result.ResultCode;
|
||||
import com.youlai.boot.config.property.SecurityProperties;
|
||||
import com.youlai.boot.core.security.model.SysUserDetails;
|
||||
import com.youlai.boot.shared.auth.model.AuthTokenResponse;
|
||||
import com.youlai.boot.shared.auth.service.TokenService;
|
||||
import com.youlai.boot.core.security.model.AuthToken;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
@@ -38,14 +37,14 @@ import java.util.stream.Collectors;
|
||||
*/
|
||||
@ConditionalOnProperty(value = "security.session.type", havingValue = "jwt")
|
||||
@Service
|
||||
public class JwtTokenService implements TokenService {
|
||||
public class JwtTokenManager implements TokenManager {
|
||||
|
||||
private final SecurityProperties securityProperties;
|
||||
private final RedisTemplate<String, Object> redisTemplate;
|
||||
private final byte[] secretKey;
|
||||
|
||||
|
||||
public JwtTokenService(SecurityProperties securityProperties, RedisTemplate<String, Object> redisTemplate) {
|
||||
public JwtTokenManager(SecurityProperties securityProperties, RedisTemplate<String, Object> redisTemplate) {
|
||||
this.securityProperties = securityProperties;
|
||||
this.redisTemplate = redisTemplate;
|
||||
this.secretKey = securityProperties.getJwt().getKey().getBytes();
|
||||
@@ -58,14 +57,14 @@ public class JwtTokenService implements TokenService {
|
||||
* @return 令牌响应对象
|
||||
*/
|
||||
@Override
|
||||
public AuthTokenResponse generateToken(Authentication authentication) {
|
||||
public AuthToken generateToken(Authentication authentication) {
|
||||
int accessTokenTimeToLive = securityProperties.getJwt().getAccessTokenTimeToLive();
|
||||
int refreshTokenTimeToLive = securityProperties.getJwt().getRefreshTokenTimeToLive();
|
||||
|
||||
String accessToken = generateToken(authentication, accessTokenTimeToLive);
|
||||
String refreshToken = generateToken(authentication, refreshTokenTimeToLive);
|
||||
|
||||
return AuthTokenResponse.builder()
|
||||
return AuthToken.builder()
|
||||
.accessToken(accessToken)
|
||||
.refreshToken(refreshToken)
|
||||
.tokenType("Bearer")
|
||||
@@ -164,7 +163,7 @@ public class JwtTokenService implements TokenService {
|
||||
*/
|
||||
|
||||
@Override
|
||||
public AuthTokenResponse refreshToken(String refreshToken) {
|
||||
public AuthToken refreshToken(String refreshToken) {
|
||||
|
||||
boolean isValid = validateToken(refreshToken);
|
||||
if (!isValid) {
|
||||
@@ -175,7 +174,7 @@ public class JwtTokenService implements TokenService {
|
||||
int accessTokenExpiration = securityProperties.getJwt().getRefreshTokenTimeToLive();
|
||||
String newAccessToken = generateToken(authentication, accessTokenExpiration);
|
||||
|
||||
return AuthTokenResponse.builder()
|
||||
return AuthToken.builder()
|
||||
.accessToken(newAccessToken)
|
||||
.refreshToken(refreshToken)
|
||||
.tokenType("Bearer")
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.youlai.boot.shared.auth.service.impl;
|
||||
package com.youlai.boot.core.security.manager;
|
||||
|
||||
import com.youlai.boot.shared.auth.model.AuthTokenResponse;
|
||||
import com.youlai.boot.shared.auth.service.TokenService;
|
||||
import com.youlai.boot.core.security.model.AuthToken;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -14,7 +13,7 @@ import org.springframework.stereotype.Service;
|
||||
*/
|
||||
@ConditionalOnProperty(value = "security.session.type", havingValue = "redis-token")
|
||||
@Service
|
||||
public class RedisTokenService implements TokenService {
|
||||
public class RedisTokenManager implements TokenManager {
|
||||
|
||||
/**
|
||||
* 生成令牌
|
||||
@@ -23,7 +22,7 @@ public class RedisTokenService implements TokenService {
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public AuthTokenResponse generateToken(Authentication authentication) {
|
||||
public AuthToken generateToken(Authentication authentication) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -56,7 +55,7 @@ public class RedisTokenService implements TokenService {
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public AuthTokenResponse refreshToken(String token) {
|
||||
public AuthToken refreshToken(String token) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.youlai.boot.shared.auth.service;
|
||||
package com.youlai.boot.core.security.manager;
|
||||
|
||||
|
||||
import com.youlai.boot.shared.auth.model.AuthTokenResponse;
|
||||
import com.youlai.boot.core.security.model.AuthToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
/**
|
||||
@@ -10,7 +10,7 @@ import org.springframework.security.core.Authentication;
|
||||
* @author Ray
|
||||
* @since 2.16.0
|
||||
*/
|
||||
public interface TokenService {
|
||||
public interface TokenManager {
|
||||
|
||||
/**
|
||||
* 生成认证 Token
|
||||
@@ -18,7 +18,7 @@ public interface TokenService {
|
||||
* @param authentication 用户认证信息
|
||||
* @return 认证 Token 响应
|
||||
*/
|
||||
AuthTokenResponse generateToken(Authentication authentication);
|
||||
AuthToken generateToken(Authentication authentication);
|
||||
|
||||
/**
|
||||
* 解析 Token 获取认证信息
|
||||
@@ -44,7 +44,7 @@ public interface TokenService {
|
||||
* @param token 刷新令牌
|
||||
* @return 认证 Token 响应
|
||||
*/
|
||||
AuthTokenResponse refreshToken(String token);
|
||||
AuthToken refreshToken(String token);
|
||||
|
||||
/**
|
||||
* 将 Token 加入黑名单
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.youlai.boot.shared.auth.model;
|
||||
package com.youlai.boot.core.security.model;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Builder;
|
||||
@@ -13,7 +13,7 @@ import lombok.Data;
|
||||
@Schema(description = "认证令牌响应对象")
|
||||
@Data
|
||||
@Builder
|
||||
public class AuthTokenResponse {
|
||||
public class AuthToken {
|
||||
|
||||
@Schema(description = "令牌类型", example = "Bearer")
|
||||
private String tokenType;
|
||||
@@ -21,7 +21,6 @@ public class AuthTokenResponse {
|
||||
@Schema(description = "访问令牌")
|
||||
private String accessToken;
|
||||
|
||||
|
||||
@Schema(description = "刷新令牌")
|
||||
private String refreshToken;
|
||||
|
||||
@@ -5,7 +5,7 @@ import com.youlai.boot.common.result.Result;
|
||||
import com.youlai.boot.shared.auth.model.RefreshTokenRequest;
|
||||
import com.youlai.boot.shared.auth.service.AuthService;
|
||||
import com.youlai.boot.shared.auth.model.CaptchaResponse;
|
||||
import com.youlai.boot.shared.auth.model.AuthTokenResponse;
|
||||
import com.youlai.boot.core.security.model.AuthToken;
|
||||
import com.youlai.boot.common.annotation.Log;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
@@ -32,12 +32,12 @@ public class AuthController {
|
||||
@Operation(summary = "登录")
|
||||
@PostMapping("/login")
|
||||
@Log(value = "登录", module = LogModuleEnum.LOGIN)
|
||||
public Result<AuthTokenResponse> login(
|
||||
public Result<AuthToken> login(
|
||||
@Parameter(description = "用户名", example = "admin") @RequestParam String username,
|
||||
@Parameter(description = "密码", example = "123456") @RequestParam String password
|
||||
) {
|
||||
AuthTokenResponse authTokenResponse = authService.login(username, password);
|
||||
return Result.success(authTokenResponse);
|
||||
AuthToken authToken = authService.login(username, password);
|
||||
return Result.success(authToken);
|
||||
}
|
||||
|
||||
@Operation(summary = "注销")
|
||||
@@ -58,17 +58,38 @@ public class AuthController {
|
||||
@Operation(summary = "刷新token")
|
||||
@PostMapping("/refresh-token")
|
||||
public Result<?> refreshToken(@RequestBody RefreshTokenRequest request) {
|
||||
AuthTokenResponse authTokenResponse = authService.refreshToken(request);
|
||||
return Result.success(authTokenResponse);
|
||||
AuthToken authToken = authService.refreshToken(request);
|
||||
return Result.success(authToken);
|
||||
}
|
||||
|
||||
@Operation(summary = "微信登录")
|
||||
@PostMapping("/wechat-login")
|
||||
@Log(value = "微信登录", module = LogModuleEnum.LOGIN)
|
||||
public Result<AuthTokenResponse> wechatLogin(
|
||||
public Result<AuthToken> wechatLogin(
|
||||
@Parameter(description = "微信授权码", example = "code") @RequestParam String code
|
||||
) {
|
||||
AuthTokenResponse loginResult = authService.wechatLogin(code);
|
||||
AuthToken loginResult = authService.wechatLogin(code);
|
||||
return Result.success(loginResult);
|
||||
}
|
||||
|
||||
|
||||
@Operation(summary = "短信验证码登录")
|
||||
@PostMapping("/sms-login")
|
||||
@Log(value = "短信验证码登录", module = LogModuleEnum.LOGIN)
|
||||
public Result<AuthToken> smsLogin(
|
||||
@Parameter(description = "手机号", example = "18888888888") @RequestParam String mobile,
|
||||
@Parameter(description = "验证码", example = "123456") @RequestParam String code
|
||||
) {
|
||||
AuthToken loginResult = authService.smsLogin(mobile, code);
|
||||
return Result.success(loginResult);
|
||||
}
|
||||
|
||||
@Operation(summary = "短信验证码登录发送短信")
|
||||
@PostMapping("/sms-login/verify-code")
|
||||
public Result<?> sendLoginVerifyCode(
|
||||
@Parameter(description = "手机号", example = "18888888888") @RequestParam String mobile
|
||||
) {
|
||||
authService.sendLoginVerifyCode(mobile);
|
||||
return Result.success();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.youlai.boot.shared.auth.service;
|
||||
|
||||
import com.youlai.boot.shared.auth.model.CaptchaResponse;
|
||||
import com.youlai.boot.shared.auth.model.AuthTokenResponse;
|
||||
import com.youlai.boot.core.security.model.AuthToken;
|
||||
import com.youlai.boot.shared.auth.model.RefreshTokenRequest;
|
||||
|
||||
/**
|
||||
@@ -19,7 +19,7 @@ public interface AuthService {
|
||||
* @param password 密码
|
||||
* @return 登录结果
|
||||
*/
|
||||
AuthTokenResponse login(String username, String password);
|
||||
AuthToken login(String username, String password);
|
||||
|
||||
/**
|
||||
* 登出
|
||||
@@ -39,7 +39,7 @@ public interface AuthService {
|
||||
* @param request 刷新令牌请求参数
|
||||
* @return 登录结果
|
||||
*/
|
||||
AuthTokenResponse refreshToken(RefreshTokenRequest request);
|
||||
AuthToken refreshToken(RefreshTokenRequest request);
|
||||
|
||||
/**
|
||||
* 微信小程序登录
|
||||
@@ -47,5 +47,12 @@ public interface AuthService {
|
||||
* @param code 微信登录code
|
||||
* @return 登录结果
|
||||
*/
|
||||
AuthTokenResponse wechatLogin(String code);
|
||||
AuthToken wechatLogin(String code);
|
||||
|
||||
/**
|
||||
* 发送短信验证码
|
||||
*
|
||||
* @param mobile 手机号
|
||||
*/
|
||||
void sendLoginVerifyCode(String mobile);
|
||||
}
|
||||
|
||||
@@ -12,11 +12,13 @@ import com.youlai.boot.config.property.CaptchaProperties;
|
||||
import com.youlai.boot.core.security.extension.WechatAuthenticationToken;
|
||||
import com.youlai.boot.core.security.util.SecurityUtils;
|
||||
import com.youlai.boot.shared.auth.enums.CaptchaTypeEnum;
|
||||
import com.youlai.boot.shared.auth.model.AuthTokenResponse;
|
||||
import com.youlai.boot.core.security.model.AuthToken;
|
||||
import com.youlai.boot.shared.auth.model.CaptchaResponse;
|
||||
import com.youlai.boot.shared.auth.model.RefreshTokenRequest;
|
||||
import com.youlai.boot.shared.auth.service.AuthService;
|
||||
import com.youlai.boot.shared.auth.service.TokenService;
|
||||
import com.youlai.boot.core.security.manager.TokenManager;
|
||||
import com.youlai.boot.shared.sms.enums.SmsTypeEnum;
|
||||
import com.youlai.boot.shared.sms.service.SmsService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
@@ -45,7 +47,9 @@ public class AuthServiceImpl implements AuthService {
|
||||
private final CodeGenerator codeGenerator;
|
||||
private final Font captchaFont;
|
||||
private final CaptchaProperties captchaProperties;
|
||||
private final TokenService tokenService;
|
||||
private final TokenManager tokenManager;
|
||||
|
||||
private final SmsService smsService;
|
||||
|
||||
/**
|
||||
* 用户名密码登录
|
||||
@@ -55,7 +59,7 @@ public class AuthServiceImpl implements AuthService {
|
||||
* @return 访问令牌
|
||||
*/
|
||||
@Override
|
||||
public AuthTokenResponse login(String username, String password) {
|
||||
public AuthToken login(String username, String password) {
|
||||
// 1. 创建用于密码认证的令牌(未认证)
|
||||
UsernamePasswordAuthenticationToken authenticationToken =
|
||||
new UsernamePasswordAuthenticationToken(username.trim(), password);
|
||||
@@ -64,7 +68,8 @@ public class AuthServiceImpl implements AuthService {
|
||||
Authentication authentication = authenticationManager.authenticate(authenticationToken);
|
||||
|
||||
// 3. 认证成功后生成 JWT 令牌,并存入 Security 上下文,供登录日志 AOP 使用(已认证)
|
||||
AuthTokenResponse authTokenResponse = tokenService.generateToken(authentication);
|
||||
AuthToken authTokenResponse =
|
||||
tokenManager.generateToken(authentication);
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
return authTokenResponse;
|
||||
}
|
||||
@@ -76,7 +81,7 @@ public class AuthServiceImpl implements AuthService {
|
||||
* @return 访问令牌
|
||||
*/
|
||||
@Override
|
||||
public AuthTokenResponse wechatLogin(String code) {
|
||||
public AuthToken wechatLogin(String code) {
|
||||
// 1. 创建用户微信认证的令牌(未认证)
|
||||
WechatAuthenticationToken authenticationToken = new WechatAuthenticationToken(code);
|
||||
|
||||
@@ -84,10 +89,30 @@ public class AuthServiceImpl implements AuthService {
|
||||
Authentication authentication = authenticationManager.authenticate(authenticationToken);
|
||||
|
||||
// 3. 认证成功后生成 JWT 令牌,并存入 Security 上下文,供登录日志 AOP 使用(已认证)
|
||||
AuthTokenResponse authTokenResponse = tokenService.generateToken(authentication);
|
||||
AuthToken authTokenResponse = tokenManager.generateToken(authentication);
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
|
||||
return authTokenResponse;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 发送短信验证码
|
||||
*
|
||||
* @param mobile 手机号
|
||||
*/
|
||||
@Override
|
||||
public void sendLoginVerifyCode(String mobile) {
|
||||
|
||||
// 随机生成4位验证码
|
||||
String verifyCode = String.valueOf((int) ((Math.random() * 9 + 1) * 1000));
|
||||
// 发送短信验证码
|
||||
smsService.sendSms(mobile, SmsTypeEnum.LOGIN, verifyCode);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -99,7 +124,7 @@ public class AuthServiceImpl implements AuthService {
|
||||
if (StrUtil.isNotBlank(token) && token.startsWith(SecurityConstants.JWT_TOKEN_PREFIX)) {
|
||||
token = token.substring(SecurityConstants.JWT_TOKEN_PREFIX.length());
|
||||
// 将JWT令牌加入黑名单
|
||||
tokenService.blacklistToken(token);
|
||||
tokenManager.blacklistToken(token);
|
||||
// 清除Security上下文
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
@@ -156,18 +181,18 @@ public class AuthServiceImpl implements AuthService {
|
||||
* @return 新的访问令牌
|
||||
*/
|
||||
@Override
|
||||
public AuthTokenResponse refreshToken(RefreshTokenRequest request) {
|
||||
public AuthToken refreshToken(RefreshTokenRequest request) {
|
||||
// 验证刷新令牌
|
||||
|
||||
String refreshToken = request.getRefreshToken();
|
||||
|
||||
boolean isValidate = tokenService.validateToken(refreshToken);
|
||||
boolean isValidate = tokenManager.validateToken(refreshToken);
|
||||
|
||||
if (!isValidate) {
|
||||
throw new BusinessException(ResultCode.REFRESH_TOKEN_INVALID);
|
||||
}
|
||||
|
||||
return tokenService.refreshToken(refreshToken);
|
||||
return tokenManager.refreshToken(refreshToken);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -128,8 +128,8 @@ public class MinioFileService implements FileService {
|
||||
/**
|
||||
* 删除文件
|
||||
*
|
||||
* @param filePath 文件路径
|
||||
* https://oss.youlai.tech/default/20221120/test.jpg
|
||||
* @param filePath 文件路径 http://localhost:9000/default/20221120/test.jpg
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
@@ -151,10 +151,8 @@ public class MinioFileService implements FileService {
|
||||
|
||||
minioClient.removeObject(removeObjectArgs);
|
||||
return true;
|
||||
} catch (ErrorResponseException | InsufficientDataException | InternalException | InvalidKeyException |
|
||||
InvalidResponseException | IOException | NoSuchAlgorithmException | ServerException |
|
||||
XmlParserException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("文件删除失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ package com.youlai.boot.shared.sms.controller;
|
||||
* @author Ray
|
||||
* @since 2.10.0
|
||||
*/
|
||||
|
||||
public class SmsController {
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.youlai.boot.shared.sms.enums;
|
||||
|
||||
import com.youlai.boot.common.base.IBaseEnum;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 短信类型枚举
|
||||
*/
|
||||
@Getter
|
||||
public enum SmsTypeEnum implements IBaseEnum<String> {
|
||||
REGISTER("register", "注册短信验证码"),
|
||||
LOGIN("login", "登录短信验证码"),
|
||||
RESET_PASSWORD("reset-password", "重置密码短信验证码");
|
||||
|
||||
private final String value;
|
||||
private final String label;
|
||||
|
||||
SmsTypeEnum(String value, String label) {
|
||||
this.value = value;
|
||||
this.label = label;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
package com.youlai.boot.shared.sms.service;
|
||||
|
||||
import com.youlai.boot.shared.sms.enums.SmsTypeEnum;
|
||||
|
||||
/**
|
||||
* 短信服务接口层
|
||||
* <p>
|
||||
* SMS = Short Message Service 短信服务
|
||||
*
|
||||
* @author Ray
|
||||
* @author Ray.Hao
|
||||
* @since 2024/8/17
|
||||
*/
|
||||
public interface SmsService {
|
||||
@@ -14,9 +14,9 @@ public interface SmsService {
|
||||
* 发送短信
|
||||
*
|
||||
* @param mobile 手机号 13388886666
|
||||
* @param templateCode 短信模板 SMS_194640010
|
||||
* @param smsType 短信模板 SMS_194640010
|
||||
* @param templateParam 模板参数 "[{"code":"123456"}]"
|
||||
* @return boolean 是否发送成功
|
||||
*/
|
||||
boolean sendSms(String mobile, String templateCode, String templateParam);
|
||||
boolean sendSms(String mobile, SmsTypeEnum smsType, String templateParam);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.aliyuncs.exceptions.ServerException;
|
||||
import com.aliyuncs.http.MethodType;
|
||||
import com.aliyuncs.profile.DefaultProfile;
|
||||
import com.youlai.boot.config.property.AliyunSmsProperties;
|
||||
import com.youlai.boot.shared.sms.enums.SmsTypeEnum;
|
||||
import com.youlai.boot.shared.sms.service.SmsService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -29,13 +30,15 @@ public class AliyunSmsService implements SmsService {
|
||||
* 发送短信验证码
|
||||
*
|
||||
* @param mobile 手机号 13388886666
|
||||
* @param templateCode 短信模板 SMS_194640010
|
||||
* @param templateParam 模板参数 "[{"code":"123456"}]"
|
||||
* @param smsType 短信模板 SMS_194640010
|
||||
* @param templateParam 模板参数 [{"code":"123456"}]
|
||||
*
|
||||
* @return boolean 是否发送成功
|
||||
*/
|
||||
@Override
|
||||
public boolean sendSms(String mobile,String templateCode,String templateParam) {
|
||||
public boolean sendSms(String mobile, SmsTypeEnum smsType, String templateParam) {
|
||||
|
||||
String templateCode = aliyunSmsProperties.getTemplates().get(smsType.getValue());
|
||||
|
||||
DefaultProfile profile = DefaultProfile.getProfile(aliyunSmsProperties.getRegionId(),
|
||||
aliyunSmsProperties.getAccessKeyId(), aliyunSmsProperties.getAccessKeySecret());
|
||||
@@ -65,8 +68,6 @@ public class AliyunSmsService implements SmsService {
|
||||
try {
|
||||
CommonResponse response = client.getCommonResponse(request);
|
||||
return response.getHttpResponse().isSuccess();
|
||||
} catch (ServerException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ClientException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.youlai.boot.common.constant.RedisConstants;
|
||||
import com.youlai.boot.common.constant.SystemConstants;
|
||||
import com.youlai.boot.shared.auth.service.TokenService;
|
||||
import com.youlai.boot.core.security.manager.TokenManager;
|
||||
import com.youlai.boot.system.enums.ContactType;
|
||||
import com.youlai.boot.common.model.Option;
|
||||
import com.youlai.boot.shared.mail.service.MailService;
|
||||
@@ -31,7 +31,6 @@ import com.youlai.boot.system.model.dto.UserExportDTO;
|
||||
import com.youlai.boot.system.model.vo.UserInfoVO;
|
||||
import com.youlai.boot.system.model.vo.UserPageVO;
|
||||
import com.youlai.boot.core.security.service.PermissionService;
|
||||
import com.youlai.boot.system.service.RoleMenuService;
|
||||
import com.youlai.boot.system.service.RoleService;
|
||||
import com.youlai.boot.system.service.UserRoleService;
|
||||
import com.youlai.boot.system.service.UserService;
|
||||
@@ -62,8 +61,6 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
|
||||
|
||||
private final UserRoleService userRoleService;
|
||||
|
||||
private final RoleMenuService roleMenuService;
|
||||
|
||||
private final RoleService roleService;
|
||||
|
||||
private final PermissionService permissionService;
|
||||
@@ -76,7 +73,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
|
||||
|
||||
private final StringRedisTemplate redisTemplate;
|
||||
|
||||
private final TokenService tokenService;
|
||||
private final TokenManager tokenManager;
|
||||
|
||||
private final UserConverter userConverter;
|
||||
|
||||
@@ -228,6 +225,21 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
|
||||
return userAuthInfo;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据手机号获取用户认证信息
|
||||
*
|
||||
* @param mobile 手机号
|
||||
* @return {@link UserAuthInfo}
|
||||
*/
|
||||
@Override
|
||||
public UserAuthInfo getUserAuthInfoByMobile(String mobile) {
|
||||
UserAuthInfo userAuthInfo = this.baseMapper.getUserAuthInfoByMobile(mobile);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据微信 OpenID 注册或绑定用户
|
||||
* <p>
|
||||
@@ -365,7 +377,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
|
||||
if (result) {
|
||||
// 加入黑名单,重新登录
|
||||
String accessToken = SecurityUtils.getTokenFromRequest();
|
||||
tokenService.blacklistToken(accessToken);
|
||||
tokenManager.blacklistToken(accessToken);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -457,7 +469,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
|
||||
* 修改当前用户邮箱
|
||||
*
|
||||
* @param form 表单数据
|
||||
* @return
|
||||
* @return true|false
|
||||
*/
|
||||
@Override
|
||||
public boolean bindEmail(EmailBindingForm form) {
|
||||
@@ -473,9 +485,9 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
|
||||
String email = form.getEmail();
|
||||
|
||||
String redisCacheKey = RedisConstants.EMAIL_VERIFICATION_CODE_PREFIX + email;
|
||||
String cachedVerificationCode = redisTemplate.opsForValue().get(redisCacheKey);
|
||||
String cachedVerifyCode = redisTemplate.opsForValue().get(redisCacheKey);
|
||||
|
||||
if (cachedVerificationCode == null || !inputVerificationCode.equals(cachedVerificationCode)) {
|
||||
if (!inputVerificationCode.equals(cachedVerifyCode)) {
|
||||
throw new BusinessException("验证码错误");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user