千家信息网

Spring Security如何优雅的增加OAuth2协议授权模式

发表于:2026-11-02 作者:千家信息网编辑
千家信息网最后更新 2026年11月02日,本篇文章给大家分享的是有关Spring Security如何优雅的增加OAuth2协议授权模式,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看
千家信息网最后更新 2026年11月02日Spring Security如何优雅的增加OAuth2协议授权模式

本篇文章给大家分享的是有关Spring Security如何优雅的增加OAuth2协议授权模式,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。

一、什么是OAuth3协议?

OAuth 2.0 是一个关于授权的开放的网络协议,是目前最流行的授权机制。

数据的所有者告诉系统,同意授权第三方应用进入系统,获取这些数据。系统从而产生一个短期的进入令牌(token),用来代替密码,供第三方应用使用。

由于授权的场景众多,OAuth 2.0 协议定义了获取令牌的四种授权方式,分别是:

  • 授权码模式:授权码模式(authorization code)是功能最完整、流程最严密的授权模式。它的特点就是通过客户端的后台服务器,与"服务提供商"的认证服务器进行互动。

  • 简化模式:简化模式(implicit grant type)不通过第三方应用程序的服务器,直接在浏览器中向认证服务器申请令牌,跳过了"授权码"这个步骤,因此得名。所有步骤在浏览器中完成,令牌对访问者是可见的,且客户端不需要认证。

  • 密码模式:密码模式(Resource Owner Password Credentials Grant)中,用户向客户端提供自己的用户名和密码。客户端使用这些信息,向"服务商提供商"索要授权。

  • 客户端模式:客户端模式(Client Credentials Grant)指客户端以自己的名义,而不是以用户的名义,向"服务提供商"进行认证。严格地说,客户端模式并不属于OAuth框架所要解决的问题。在这种模式中,用户直接向客户端注册,客户端以自己的名义要求"服务提供商"提供服务,其实不存在授权问题。

四种授权模式分别使用不同的 grant_type 来区分

二、为什么要自定义授权类型?

虽然 OAuth3 协议定义了4种标准的授权模式,但是在实际开发过程中还是远远满足不了各种变态的业务场景,需要我们去扩展。

例如增加图形验证码、手机验证码、手机号密码登录等等的场景

而常见的做法都是通过增加 过滤器Filter 的方式来扩展 Spring Security 授权,但是这样的实现方式有两个问题:

  1. 脱离了 OAuth3 的管理

  2. 不灵活:例如系统使用 密码模式 授权,网页版需要增加图形验证码校验,但是手机端APP又不需要的情况下,使用增加 Filter 的方式去实现就比较麻烦了。

所以目前在 Spring Security 中比较优雅和灵活的扩展方式就是通过自定义 grant_type 来增加授权模式。

三、实现思路

在扩展之前首先需要先了解 Spring Security 的整个授权流程,我以 密码模式 为例去展开分析,如下图所示

3.1. 流程分析

整个授权流程关键点分为以下两个部分:

第一部分:关于授权类型 grant_type 的解析

  1. 每种 grant_type 都会有一个对应的 TokenGranter 实现类。

  2. 所有 TokenGranter 实现类都通过 CompositeTokenGranter 中的 tokenGranters 集合存起来。

  3. 然后通过判断 grantType 参数来定位具体使用那个 TokenGranter 实现类来处理授权。

第二部分:关于授权登录逻辑

  1. 每种 授权方式 都会有一个对应的 AuthenticationProvider 实现类来实现。

  2. 所有 AuthenticationProvider 实现类都通过 ProviderManager 中的 providers 集合存起来。

  3. TokenGranter 类会 new 一个 AuthenticationToken 实现类,如 UsernamePasswordAuthenticationToken 传给 ProviderManager 类。

  4. 而 ProviderManager 则通过 AuthenticationToken 来判断具体使用那个 AuthenticationProvider 实现类来处理授权。

  5. 具体的登录逻辑由 AuthenticationProvider 实现类来实现,如 DaoAuthenticationProvider。

3.2. 扩展分析

根据上面的流程,扩展分为以下两种场景

场景一:只对原有的授权逻辑进行增强或者扩展,如:用户名密码登录前增加图形验证码校验。

该场景需要定义一个新的 grantType 类型,并新增对应的 TokenGranter 实现类 添加扩展内容,然后加到 CompositeTokenGranter 中的 tokenGranters 集合里即可。

参考代码:PwdImgCodeGranter.java

场景二:新加一种授权方式,如:手机号加密码登录。

该场景需要实现以下内容:

  1. 定义一个新的 grantType 类型,并新增对应的 TokenGranter 实现类添加到 CompositeTokenGranter 中的 tokenGranters 集合里

  2. 新增一个 AuthenticationToken 实现类,用于存放该授权所需的信息。

  3. 新增一个 AuthenticationProvider 实现类 实现授权的逻辑,并重写 supports 方法绑定步骤二的 AuthenticationToken 实现类

参考代码:MobilePwdGranter.java

四、代码实现

下面以 场景二 新增手机号加密码授权方式为例,展示核心的代码实现

4.1. 创建 AuthenticationToken 实现类

创建 MobileAuthenticationToken 类,用于存储手机号和密码信息

public class MobileAuthenticationToken extends AbstractAuthenticationToken {        private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;        private final Object principal;        private Object credentials;        public MobileAuthenticationToken(String mobile, String password) {                super(null);                this.principal = mobile;                this.credentials = password;                setAuthenticated(false);        }        public MobileAuthenticationToken(Object principal, Object credentials,                                                                         Collection authorities) {                super(authorities);                this.principal = principal;                this.credentials = credentials;                super.setAuthenticated(true);        }        @Override        public Object getCredentials() {                return this.credentials;        }        @Override        public Object getPrincipal() {                return this.principal;        }        @Override        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();        }}

4.2. 创建 AuthenticationProvider 实现类

创建 MobileAuthenticationProvider 类,实现登录逻辑,并绑定 MobileAuthenticationToken 类

@Setterpublic class MobileAuthenticationProvider implements AuthenticationProvider {    private ZltUserDetailsService userDetailsService;    private PasswordEncoder passwordEncoder;    @Override    public Authentication authenticate(Authentication authentication) {        MobileAuthenticationToken authenticationToken = (MobileAuthenticationToken) authentication;        String mobile = (String) authenticationToken.getPrincipal();        String password = (String) authenticationToken.getCredentials();        UserDetails user = userDetailsService.loadUserByMobile(mobile);        if (user == null) {            throw new InternalAuthenticationServiceException("手机号或密码错误");        }        if (!passwordEncoder.matches(password, user.getPassword())) {            throw new BadCredentialsException("手机号或密码错误");        }        MobileAuthenticationToken authenticationResult = new MobileAuthenticationToken(user, password, user.getAuthorities());        authenticationResult.setDetails(authenticationToken.getDetails());        return authenticationResult;    }    @Override    public boolean supports(Class authentication) {        return MobileAuthenticationToken.class.isAssignableFrom(authentication);    }}

4.3. 创建 TokenGranter 实现类

创建 MobilePwdGranter 类并定义 grant_type 的值为 mobile_password

public class MobilePwdGranter extends AbstractTokenGranter {    private static final String GRANT_TYPE = "mobile_password";    private final AuthenticationManager authenticationManager;    public MobilePwdGranter(AuthenticationManager authenticationManager, AuthorizationServerTokenServices tokenServices            , ClientDetailsService clientDetailsService, OAuth3RequestFactory requestFactory) {        super(tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);        this.authenticationManager = authenticationManager;    }    @Override    protected OAuth3Authentication getOAuth3Authentication(ClientDetails client, TokenRequest tokenRequest) {        Map parameters = new LinkedHashMap<>(tokenRequest.getRequestParameters());        String mobile = parameters.get("mobile");        String password = parameters.get("password");        parameters.remove("password");        Authentication userAuth = new MobileAuthenticationToken(mobile, password);        ((AbstractAuthenticationToken) userAuth).setDetails(parameters);        userAuth = authenticationManager.authenticate(userAuth);        if (userAuth == null || !userAuth.isAuthenticated()) {            throw new InvalidGrantException("Could not authenticate mobile: " + mobile);        }        OAuth3Request storedOAuth3Request = getRequestFactory().createOAuth3Request(client, tokenRequest);        return new OAuth3Authentication(storedOAuth3Request, userAuth);    }}

4.4. 加到 CompositeTokenGranter 中的集合里

// 添加手机号加密码授权模式tokenGranters.add(new MobilePwdGranter(authenticationManager, tokenServices, clientDetailsService, requestFactory));

4.5. 测试

使用以下地址,指定 grant_type 为 mobile_password 进行授权获取 access_token

/oauth/token?grant_type=mobile_password&mobile={mobile}&password={password}

以上就是Spring Security如何优雅的增加OAuth2协议授权模式,小编相信有部分知识点可能是我们日常工作会见到或用到的。希望你能通过这篇文章学到更多知识。更多详情敬请关注行业资讯频道。

0