美文网首页
Spring Security oAuth2新增鉴权方式gran

Spring Security oAuth2新增鉴权方式gran

作者: wds_94 | 来源:发表于2024-12-03 20:13 被阅读0次

默认鉴权方式

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'

相关文章

网友评论

      本文标题:Spring Security oAuth2新增鉴权方式gran

      本文链接:https://www.haomeiwen.com/subject/flhusjtx.html