默认鉴权方式
Spring Security oAuth2 默认登录方式为账号/密码,即grant_type = password,这种方式只需要输入账号密码,即可完成授权。
/**
* 用户详细信息
* App端密码登录
*/
@Slf4j
@RequiredArgsConstructor
@Component
public class AppPasswordUserDetailsServiceImpl implements UserDetailsService {
private final UserDetailsService miniDefaultUserDetailsServiceImpl;
private final UserFeign userFeign;
private final RedisTemplate<String, String> redisTemplate;
private final LoginAuthConfig loginAuthConfig;
/**
* 邮箱登录
*
* @param email 邮箱
* @return
*/
@Override
@SneakyThrows
public UserDetails loadUserByUsername(String email) {
R<UserLoginInfoResDTO> result = userFeign.getLoginInfoByWalletAddrOrEmail(email, SecurityConstants.FROM_IN);
UserLoginInfoResDTO userLoginInfo = result.getData();
if (Objects.isNull(userLoginInfo)) {
throw new BusinessException(UserBusinessCodeEnum.ACCOUNT_OR_PASSWORD_ERROR);
}
if (StringUtils.isBlank(userLoginInfo.getPassword())) {
if (UserInfoEnums.GoogleVerifyStatusEnum.BIND.getCode().equals(userLoginInfo.getGoogleVerifyStatus())) {
throw new BusinessException(UserBusinessCodeEnum.ACCOUNT_OR_PASSWORD_OR_GOOGLE_CODE_ERROR);
} else {
throw new BusinessException(UserBusinessCodeEnum.ACCOUNT_OR_PASSWORD_ERROR);
}
}
// 构造security用户
boolean enabled = GeneralEnums.SwitchValueTypeEnum.ON.getCode().equals(userLoginInfo.getLoginSwitch());
String lockedAccountKey = String.format(UserRedisKeyConstants.CUSTOMER_PASSWORD_LOGIN_FREEZE_ACCOUNT_KEY_PREFIX, email);
boolean accountNonLocked = Objects.isNull(redisTemplate.opsForValue().get(lockedAccountKey));
String googleVerifyKey = StringUtils.isBlank(userLoginInfo.getGoogleVerifyKey()) ?
"" : userLoginInfo.getGoogleVerifyKey();
if (!enabled) {
throw new BusinessException(UserBusinessCodeEnum.ACCOUNT_IS_DISABLED);
}
if (!accountNonLocked) {
throw new BusinessException(UserBusinessCodeEnum.TOO_MANY_FAILED_LOGIN_ATTEMPTS_ACCOUNT_LOCKED);
}
//校验谷歌验证码
checkGoogleCode(WebUtils.getGoogleCode(), userLoginInfo.getGoogleVerifyStatus(), userLoginInfo.getGoogleVerifyKey(), email);
return new MiniUser(
userLoginInfo.getUid(),
userLoginInfo.getUsername(),
userLoginInfo.getPassword(),
true,
true,
true,
true,
AuthorityUtils.NO_AUTHORITIES,
googleVerifyKey,
userLoginInfo.getUsername()
);
}
private void checkGoogleCode(String googleCode, String googleVerifyStatus, String googleVerifyKey, String username) {
if (UserInfoEnums.GoogleVerifyStatusEnum.UNBIND.getCode().equals(googleVerifyStatus)) {
return;
}
if (StringUtils.isBlank(googleCode)) {
throw new BusinessException(UserBusinessCodeEnum.GOOGLE_CAPTCHA_CANNOT_BE_EMPTY);
}
if (!GoogleAuthenticator.authCode(googleCode, googleVerifyKey)) {
String googleCodeErrorCountKey = String.format(UserRedisKeyConstants.CUSTOMER_PASSWORD_LOGIN_GOOGLE_CODE_ERRORS_NUMBER_KEY_PREFIX, username);
String oldGoogleCodeErrorCountStr = redisTemplate.opsForValue().get(googleCodeErrorCountKey);
int newGoogleCodeErrorCount = Objects.isNull(oldGoogleCodeErrorCountStr) ? 1 : Integer.parseInt(oldGoogleCodeErrorCountStr) + 1;
redisTemplate.opsForValue().set(googleCodeErrorCountKey, String.valueOf(newGoogleCodeErrorCount));
if (newGoogleCodeErrorCount >= loginAuthConfig.getCustomerPasswordLoginMaxGoogleCodeErrorNumber()) {
String passwordLoginFreezeAccountKey = String.format(
UserRedisKeyConstants.CUSTOMER_PASSWORD_LOGIN_FREEZE_ACCOUNT_KEY_PREFIX, username);
redisTemplate.opsForValue().set(passwordLoginFreezeAccountKey, username,
loginAuthConfig.getCustomerPasswordLoginFreezeAccountTime(), TimeUnit.SECONDS);
}
if (Long.parseLong(getErrorsNumber(username)) < loginAuthConfig.getCustomerPasswordLoginMaxGoogleCodeErrorNumber()) {
throw new BusinessException(UserBusinessCodeEnum.ACCOUNT_OR_PASSWORD_OR_GOOGLE_CODE_ERROR);
} else {
redisTemplate.delete(String.format(UserRedisKeyConstants.CUSTOMER_PASSWORD_LOGIN_GOOGLE_CODE_ERRORS_NUMBER_KEY_PREFIX, username));
throw new BusinessException(UserBusinessCodeEnum.TOO_MANY_FAILED_LOGIN_ATTEMPTS_ACCOUNT_LOCKED);
}
}
}
private String getErrorsNumber(String username) {
String errorsNumberKey = String.format(UserRedisKeyConstants.CUSTOMER_PASSWORD_LOGIN_GOOGLE_CODE_ERRORS_NUMBER_KEY_PREFIX, username);
return redisTemplate.opsForValue().get(errorsNumberKey);
}
/**
* check-token 使用
*
* @param miniUser user
* @return UserDetails
*/
@Override
public UserDetails loadUserByUser(MiniUser miniUser) {
return miniDefaultUserDetailsServiceImpl.loadUserByUsername(miniUser.getUsername());
}
/**
* 支持所有的 mobile 类型
*
* @param clientId 目标客户端
* @param grantType 授权类型
* @return true/false
*/
@Override
public boolean support(String clientId, String grantType) {
return SecurityConstants.CLIENT_APP.equals(clientId)
&& SecurityConstants.GRANT_PASSWORD.equals(grantType);
}
@Override
public int getOrder() {
return Integer.MIN_VALUE + 1;
}
}
新增鉴权方式 验证码/其他验签
新增鉴权类型流程:
- 1.新增一个类继承AbstractTokenGranter 重写方法getOAuth2Authentication
- 2.在AuthorizationServerConfiguration#setTokenGranter方法中增加新的granter
- 3.CustomAppAuthenticationProvider#authenticate 鉴权方法修改
- 4.数据库增加sys_oauth_client_details customer的鉴权类型
新增的授权方式 grant_type =xxx 在客户端请求curl如下:
curl --location --request POST 'http://127.0.0.1:8888/auth/oauth/token?operation_type=1&randomStr=blockPuzzle&grant_type=xxx' \
--header 'Authorization: Basic Y3VzdG9tZXI6Y3VzdG9tZXI=' \
--header 'client_id: customer' \
--header 'grant_type: xxx' \
--header 'token: null' \
--header 'isToken: false' \
--header 'clientType: PC' \
--header 'sign: 111' \
--header 'version: 1' \
--header 'device: web' \
--header 'operatingSystem: Windows 10 x64' \
--header 'timestamp: 1709106368982' \
--header 'User-Agent: Apifox/1.0.0 (https://apifox.com)' \
--data-urlencode 'username=wds' \
--data-urlencode 'password=I5yAk12oQ9fC123'









网友评论