时间:2021-05-20
一、什么是OAuth2协议?
OAuth 2.0 是一个关于授权的开放的网络协议,是目前最流行的授权机制。
数据的所有者告诉系统,同意授权第三方应用进入系统,获取这些数据。系统从而产生一个短期的进入令牌(token),用来代替密码,供第三方应用使用。
由于授权的场景众多,OAuth 2.0 协议定义了获取令牌的四种授权方式,分别是:
四种授权模式分别使用不同的 grant_type 来区分
二、为什么要自定义授权类型?
虽然 OAuth2 协议定义了4种标准的授权模式,但是在实际开发过程中还是远远满足不了各种变态的业务场景,需要我们去扩展。
例如增加图形验证码、手机验证码、手机号密码登录等等的场景
而常见的做法都是通过增加 过滤器Filter 的方式来扩展 Spring Security 授权,但是这样的实现方式有两个问题:
所以目前在 Spring Security 中比较优雅和灵活的扩展方式就是通过自定义 grant_type 来增加授权模式。
三、实现思路
在扩展之前首先需要先了解 Spring Security 的整个授权流程,我以 密码模式 为例去展开分析,如下图所示
3.1. 流程分析
整个授权流程关键点分为以下两个部分:
第一部分:关于授权类型 grant_type 的解析
第二部分:关于授权登录逻辑
具体的登录逻辑由 AuthenticationProvider 实现类来实现,如 DaoAuthenticationProvider。
3.2. 扩展分析
根据上面的流程,扩展分为以下两种场景
场景一:只对原有的授权逻辑进行增强或者扩展,如:用户名密码登录前增加图形验证码校验。
该场景需要定义一个新的 grantType 类型,并新增对应的 TokenGranter 实现类 添加扩展内容,然后加到 CompositeTokenGranter 中的 tokenGranters 集合里即可。
参考代码:PwdImgCodeGranter.java
场景二:新加一种授权方式,如:手机号加密码登录。
该场景需要实现以下内容:
参考代码: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<? extends GrantedAuthority> 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, OAuth2RequestFactory requestFactory) { super(tokenServices, clientDetailsService, requestFactory, GRANT_TYPE); this.authenticationManager = authenticationManager; } @Override protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) { Map<String, String> 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); } OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest); return new OAuth2Authentication(storedOAuth2Request, 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}
五、参考样例
详细的代码实现可以参考
https://gitee.com/zlt2000/microservices-platform/tree/master/zlt-uaa
到此这篇关于Spring Security如何优雅的增加OAuth2协议授权模式的文章就介绍到这了,更多相关Spring Security OAuth2内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
spring-security里自带了oauth2,正好YIIU里也用到了spring-security做权限部分,那为何不直接集成上第三方登录呢?然后我开始了
接上文SpringCloud下基于OAUTH2认证授权的实现,我们将基于SpringCloud实现OAUTH2的注销功能。1增加自定义注销Endpoint所谓注
OAuth2的概念OAuth是一个关于授权的开放网络标准,OAuth2是其2.0版本。它规定了四种操作流程(授权模式)来确保安全应用场景有第三方应用的接入、微服
前言经过一段时间的学习Oauth2,在网上也借鉴学习了一些大牛的经验,推荐在学习的过程中多看几遍阮一峰的《理解OAuth2.0》,经过对Oauth2的多种方式的
OAuth2.0是什么OAuth的英文全称是OpenAuthorization,它是一种开放授权协议。OAuth目前共有2个版本,2007年12月的1.0版(之