Oauth如何增加自定义登录模式

记录下学习oauth时,如何自定义grant_type

创建一个新的Token模式,并继承AbstractAuthenticationToken,代码如下

/**
 * 自定义Sms的AppAuthenticationToken
 *
 */
public class SmsCodeAuthenticationToken extends AbstractAuthenticationToken {
	private static final long serialVersionUID = -2721661528106186767L;
	private final Object principal;

	public SmsCodeAuthenticationToken(String mobile) {
		super(null);
		this.principal = mobile;
		setAuthenticated(false);
	}

	public SmsCodeAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {
		super(authorities);
		this.principal = principal;
		super.setAuthenticated(true);
	}

	@Override
	public Object getCredentials() {
		return this.principal;
	}

	@Override
	public Object getPrincipal() {
		return this.principal;
	}

	@Override
	@SneakyThrows
	public void setAuthenticated(boolean isAuthenticated) {
		if (isAuthenticated) {
			throw new IllegalArgumentException(
					"Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
		}

		super.setAuthenticated(false);
	}

	@Override
	public void eraseCredentials() {
		super.eraseCredentials();
	}
}

2.创建一个新的Provider,并实现AuthenticationProvider

@AllArgsConstructor
public class SmsCodeProvider implements AuthenticationProvider {
    private final UserDetailService userDetailService;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        SmsCodeAuthenticationToken authenticationToken = (SmsCodeAuthenticationToken) authentication;
        /**
         * 调用 {@link UserDetailsService}
         */
        UserDetails user = userDetailService.loadUserByUserMobile((String) authenticationToken.getPrincipal());

        if (Objects.isNull(user)) {
            throw new InternalAuthenticationServiceException("手机号或验证码错误");
        }

        SmsCodeAuthenticationToken authenticationResult = new SmsCodeAuthenticationToken(user, user.getAuthorities());
        Object details = authenticationToken.getDetails();
        authenticationResult.setDetails(details);

        return authenticationResult;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return SmsCodeAuthenticationToken.class.isAssignableFrom(aClass);
    }
}

3.实现Provider,需要继承AbstractTokenGranter

/**
 * 自定义验证码登录
 */
public class SmsCodeTokenGranter extends AbstractTokenGranter {

    private static final String GRANT_TYPE = "sms";

    private final AuthenticationManager authenticationManager;

    private RedisUtil redisService;


    private final String CODE_ = "lione.sms.code.";
    protected SmsCodeTokenGranter(AuthenticationManager authenticationManager,AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, String grantType) {
        super(tokenServices, clientDetailsService, requestFactory, grantType);
        this.authenticationManager = authenticationManager;
    }

    public SmsCodeTokenGranter(AuthenticationManager authenticationManager,
                                  AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService,
                                  OAuth2RequestFactory requestFactory, RedisUtil redisService) {
        this(authenticationManager, tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
        this.redisService = redisService;
    }

    /**
     * 实现方法
     * @param client
     * @param tokenRequest
     * @return
     */
    @Override
    protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
        Map<String, String> parameters = new LinkedHashMap<String, String>(tokenRequest.getRequestParameters());
        String mobile = parameters.get("mobile");
        String code = parameters.get("code");
        if (StringUtils.isBlank(code)) {
            throw new InvalidGrantException("验证码为空");
        }

        // 校验key
        Boolean aBoolean = redisService.hasKey(CODE_ + mobile);
        if (!aBoolean) {
            throw new InvalidGrantException("请发送验证码");
        }
        String s = redisService.get(CODE_ + mobile);
        if (!s.equals(code)) {
            throw new InvalidGrantException("验证码有误");
        }

        Authentication userAuth = new SmsCodeAuthenticationToken(mobile);
        // TODO: 2023/8/31 赋值  关键 
        ((AbstractAuthenticationToken) userAuth).setDetails(parameters);
        try {
            userAuth = authenticationManager.authenticate(userAuth);
        } catch (AccountStatusException | BadCredentialsException ase) {
            //covers expired, locked, disabled cases (mentioned in section 5.2, draft 31)
            throw new InvalidGrantException(ase.getMessage());
        }
        // If the username/password are wrong the spec says we should send 400/invalid grant

        if (userAuth == null || !userAuth.isAuthenticated()) {
            throw new InvalidGrantException("Could not authenticate user: " + mobile);
        }

        OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
        return new OAuth2Authentication(storedOAuth2Request, userAuth);
    }

}

4.将创建的Provider注册到config中

4.1创建SecurityConfig并继承SecurityConfigurerAdapter
@Slf4j
@Component
public class SmsCodEAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {

	@Autowired
	private UserDetailService userDetailsService;

	@Override
	public void configure(HttpSecurity http) {

		// 获取验证码提供者
		SmsCodeProvider appAuthenticationProvider = new SmsCodeProvider(userDetailsService);
		// 将短信验证码校验器注册到 HttpSecurity
		http.authenticationProvider(appAuthenticationProvider);
	}
}
4.2 并在WebSecurityConfigurerAdapter继承类中添加
    @Resource
    private SmsCodEAuthenticationSecurityConfig smsCodEAuthenticationSecurityConfig;

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry config
                = http.requestMatchers().anyRequest()
                .and()
                .formLogin()
                .and()
                .apply(noPasswordAuthenticationSecurityConfig)
                .and()
                .apply(smsCodEAuthenticationSecurityConfig).and()
                .authorizeRequests();
        config.antMatchers(SECURITY_ENDPOINTS).permitAll();
        config
                //任何请求
                .anyRequest()
                //都需要身份认证
                .authenticated()
                //csrf跨站请求
                .and()
                .csrf().disable();

    }

6.最后再AuthorizationServerConfigurerAdapter实现类中添加方法

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
        // 配置Token的存储方式
        endpoints.exceptionTranslator(oAuthServerWebResponseExceptionTranslator());
        // 注入WebSecurityConfig配置的bean
        endpoints.authenticationManager(authenticationManager).tokenServices(tokenServices());
        endpoints.tokenGranter(tokenGranter(endpoints));
    }

    /**
     * 重点
     * 先获取已经有的五种授权,然后添加我们自己的进去
     *
     * @param endpoints AuthorizationServerEndpointsConfigurer
     * @return TokenGranter
     */
    private TokenGranter tokenGranter(final AuthorizationServerEndpointsConfigurer endpoints) {
        List<TokenGranter> granters = new ArrayList<>(Collections.singletonList(endpoints.getTokenGranter()));
        granters.add(new NOPasswordTokenGranter(authenticationManager, endpoints.getTokenServices(), endpoints.getClientDetailsService(),
                endpoints.getOAuth2RequestFactory(), redisUtil));
        granters.add(new SmsCodeTokenGranter(authenticationManager, endpoints.getTokenServices(), endpoints.getClientDetailsService(),
                endpoints.getOAuth2RequestFactory(), redisUtil));
        return new CompositeTokenGranter(granters);
    }

总结

创建一个新的登录类型时,需要分别实现AuthenticationProviderAbstractTokenGranterAbstractAuthenticationTokenSecurityConfigurerAdapter,最后需要再WebSecurityConfigurerAdapter的继承类中添加

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值