在 oauth2 中已经默认有的授权模式中有4种:

  • 授权码模式
  • 隐式授权模式
  • 密码模式
  • 客户端模式

但是实际使用过程中万一我们要基于短信授权,或者基于token 直接授权该怎么办?

这里我们可以先看下源码

我们将要改的地方

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {    @Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {}}

源码中默认授权模式的代码

public final class AuthorizationServerEndpointsConfigurer {//存储 授权模式对象private TokenGranter tokenGranter;....省略部分代码....//获取 授权模式存储对象private TokenGranter tokenGranter() {if (this.tokenGranter == null) {this.tokenGranter = new TokenGranter() {private CompositeTokenGranter delegate;public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {if (this.delegate == null) {this.delegate = new CompositeTokenGranter(AuthorizationServerEndpointsConfigurer.this.getDefaultTokenGranters());}return this.delegate.grant(grantType, tokenRequest);}};}return this.tokenGranter;}//默认的授权模式集合private List<TokenGranter> getDefaultTokenGranters() {ClientDetailsService clientDetails = this.clientDetailsService();AuthorizationServerTokenServices tokenServices = this.tokenServices();AuthorizationCodeServices authorizationCodeServices = this.authorizationCodeServices();OAuth2RequestFactory requestFactory = this.requestFactory();List<TokenGranter> tokenGranters = new ArrayList();tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices, clientDetails, requestFactory));tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetails, requestFactory));ImplicitTokenGranter implicit = new ImplicitTokenGranter(tokenServices, clientDetails, requestFactory);tokenGranters.add(implicit);tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, clientDetails, requestFactory));if (this.authenticationManager != null) {tokenGranters.add(new ResourceOwnerPasswordTokenGranter(this.authenticationManager, tokenServices, clientDetails, requestFactory));}return tokenGranters;}....省略部分代码....
}

我们不难发现,OAuth2 通过 tokenGranter()方法来确认有哪些授权模式,那么是不是意味着我们只要修改对应 tokenGranter方法就可以来添加我们的授权模式了?没错,但是如果需要之前默认的模式,需要添加上。

那么我们应该怎么去写呢,直接找一个模仿一个就行了,

梳理下我们的假设需求:

(1)验证header或者cookies或者参数中是否带有user-token

(2)user-token进行校验

(3)直接附带授权码跳转三方页面(这里为了方便测试就直接返回json值,原理差不多)

模仿其他的模式,写一个自定义的手机号码授权登陆,


import com.example.demooauth.Token.MobileCodeAuthenticationToken;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.AccountStatusException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.token.AbstractTokenGranter;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;import java.util.LinkedHashMap;
import java.util.Map;/*** 自定义grant_type模式-手机号短信验证模式***/
public class MobileCodeTokenGranter extends AbstractTokenGranter {private static final String GRANT_TYPE = "mobile";private final AuthenticationManager authenticationManager;public MobileCodeTokenGranter(AuthenticationManager authenticationManager,AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) {this(authenticationManager, tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);}private MobileCodeTokenGranter(AuthenticationManager authenticationManager, AuthorizationServerTokenServices tokenServices,ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, String grantType) {super(tokenServices, clientDetailsService, requestFactory, grantType);this.authenticationManager = authenticationManager;}@Overrideprotected 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");Authentication userAuth = new MobileCodeAuthenticationToken(mobile, code);((AbstractAuthenticationToken) userAuth).setDetails(parameters);try {userAuth = authenticationManager.authenticate(userAuth);} catch (AccountStatusException ase) {//covers expired, locked, disabled cases (mentioned in section 5.2, draft 31)throw new InvalidGrantException(ase.getMessage());} catch (BadCredentialsException e) {// If the username/password are wrong the spec says we should send 400/invalid grantthrow new InvalidGrantException(e.getMessage());}if (userAuth == null || !userAuth.isAuthenticated()) {throw new InvalidGrantException("Could not authenticate mobile: " + mobile);}OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);return new OAuth2Authentication(storedOAuth2Request, userAuth);}
}

授权模式如何加入呢?

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {/*** 创建grant_type列表* @param endpoints* @return*/private TokenGranter tokenGranter(AuthorizationServerEndpointsConfigurer endpoints) {List<TokenGranter> list = new ArrayList<>();// 这里配置密码模式if (authenticationManager != null) {list.add(new ResourceOwnerPasswordTokenGranter(authenticationManager,endpoints.getTokenServices(),endpoints.getClientDetailsService(),endpoints.getOAuth2RequestFactory()));}//刷新token模式、list.add(new RefreshTokenGranter(endpoints.getTokenServices(),endpoints.getClientDetailsService(),endpoints.getOAuth2RequestFactory()));//自定义手机号验证码模式、list.add(new MobileCodeTokenGranter(authenticationManager,endpoints.getTokenServices(),endpoints.getClientDetailsService(),endpoints.getOAuth2RequestFactory()));//授权码模式、list.add(new AuthorizationCodeTokenGranter(endpoints.getTokenServices(),endpoints.getAuthorizationCodeServices(),endpoints.getClientDetailsService(),endpoints.getOAuth2RequestFactory()));//、简化模式 list.add(new ImplicitTokenGranter(endpoints.getTokenServices(),endpoints.getClientDetailsService(),endpoints.getOAuth2RequestFactory()));//客户端模式list.add(new ClientCredentialsTokenGranter(endpoints.getTokenServices(), endpoints.getClientDetailsService(),  endpoints.getOAuth2RequestFactory()));return new CompositeTokenGranter(list);}//添加到配置中@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {endpoints// 具体可以查询AuthorizationServerEndpointsConfigurer中的getDefaultTokenGranters方法,建议放在最末尾,因为依赖endpoints中的参数.tokenGranter(tokenGranter(endpoints));}
}

到这基本上是完整了自定义一个授权模式,但是问题是,我要处理逻辑呀~~ 接着来


import com.example.demooauth.Token.MobileCodeAuthenticationToken;
import com.example.demooauth.constant.RedisConstant;
import com.example.demooauth.service.UserDetailsServiceImpl;
import lombok.extern.log4j.Log4j2;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;/*** 手机模式认证提供者,手机验证码模式认证工作通过该类完成**/
@Log4j2
public class MobileCodeAuthenticationProvider implements AuthenticationProvider, MessageSourceAware {private StringRedisTemplate stringRedisTemplate;private UserDetailsServiceImpl userDetailsService;private MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();/*** 是否隐藏用户未发现异常,默认为true,为true返回的异常信息为BadCredentialsException*/private boolean hideUserNotFoundExceptions = true;@Overridepublic void setMessageSource(MessageSource messageSource) {this.messages = new MessageSourceAccessor(messageSource);}@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {String grant_type = getRequest().getParameter("grant_type");//代码逻辑处理处会循环调用provider 直到遇到能处理的provider类,典型的责任链模式if(!"mobile".equals(grant_type)){return null;}String mobile = (String) authentication.getPrincipal();if (mobile == null) {throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Missing mobile"));}String code = (String) authentication.getCredentials();if (code == null) {log.error("缺失code参数");throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Missing code"));}String cacheCode = stringRedisTemplate.opsForValue().get(RedisConstant.SMS_CODE_PREFIX + mobile);cacheCode = "9527";if (cacheCode == null || !cacheCode.equals(code)) {log.error("短信验证码错误");throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Invalid code"));}//清除redis中的短信验证码//stringRedisTemplate.delete(RedisConstant.SMS_CODE_PREFIX + mobile);UserDetails user;try {user = userDetailsService.loadUserByMobile(mobile);} catch (UsernameNotFoundException var6) {log.info("手机号:" + mobile + "未查到用户信息");if (this.hideUserNotFoundExceptions) {throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));}throw var6;}check(user);MobileCodeAuthenticationToken authenticationToken = new MobileCodeAuthenticationToken(user, code, user.getAuthorities());authenticationToken.setDetails(authenticationToken.getDetails());return authenticationToken;}/*** 指定该认证提供者验证Token对象* @param aClass* @return*/@Overridepublic boolean supports(Class<?> aClass) {return MobileCodeAuthenticationToken.class.isAssignableFrom(aClass);}/*** 账号禁用、锁定、超时校验** @param user*/private void check(UserDetails user) {if (!user.isAccountNonLocked()) {throw new LockedException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.locked", "User account is locked"));} else if (!user.isEnabled()) {throw new DisabledException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.disabled", "User is disabled"));} else if (!user.isAccountNonExpired()) {throw new AccountExpiredException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.expired", "User account has expired"));}}public void setStringRedisTemplate(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate = stringRedisTemplate;}public void setHideUserNotFoundExceptions(boolean hideUserNotFoundExceptions) {this.hideUserNotFoundExceptions = hideUserNotFoundExceptions;}public void setUserDetailsService(UserDetailsServiceImpl userDetailsService) {this.userDetailsService = userDetailsService;}public static HttpServletRequest getRequest(){ServletRequestAttributes ra= (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request =  ra.getRequest();return request;}
}

这是是提供了一个自定义处理逻辑的方法,但是需要加入到去授权逻辑中去


/*** security web安全配置,spring-cloud-starter-oauth2依赖于security* 默认情况下SecurityConfigurerAdapter执行比ResourceServerConfig先*/
@Configuration
@EnableWebSecurity()
public class SecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {@Autowiredprivate UserDetailsServiceImpl userDetailsService;@Autowiredprivate StringRedisTemplate stringRedisTemplate;@Autowiredprivate IgnoreLogoutFilter ignoreLogoutFilter;/*** 配置认证管理器** @return* @throws Exception*/@Bean@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}/*** 配置密码加密对象(解密时会用到PasswordEncoder的matches判断是否正确)* 用户的password和客户端clientSecret用到,所以存的时候存该bean encode过的密码** @return*/@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}/*** 这里是对认证管理器的添加配置** @param auth* @throws Exception*/@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.authenticationProvider(provider()).userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());}@Overridepublic void configure(WebSecurity web) throws Exception {web.ignoring().antMatchers("/css/**", "/static/**");}/*** 安全请求配置,这里配置的是security的部分,这里配置全部通过,安全拦截在资源服务的配置文件中配置,* 要不然访问未验证的接口将重定向到登录页面,前后端分离的情况下这样并不友好,无权访问接口返回相关错误信息即可** @param http* @return void*/@Overrideprotected void configure(HttpSecurity http) throws Exception {http.formLogin().loginPage("/login").permitAll().and().authorizeRequests().anyRequest().permitAll().and().csrf().disable().cors().and().addFilterAt(ignoreLogoutFilter, LogoutFilter.class);}/*** 自定义手机验证码认证提供者** @return*/@Beanpublic MobileCodeAuthenticationProvider provider() {MobileCodeAuthenticationProvider provider = new MobileCodeAuthenticationProvider();provider.setStringRedisTemplate(stringRedisTemplate);provider.setHideUserNotFoundExceptions(false);provider.setUserDetailsService(userDetailsService);return provider;}}

到此基本上完全搞定

到这是我根据这几天看别人的demo以及 跟踪源码得出的结论,还有很多不明之处,需要进一步调整,我也会持续修改文章,现在还比较粗糙。

Spring Security Oauth2 如何增加自定义授权模式相关推荐

  1. 从零开始超详细的Spring Security OAuth2.0实现分布式系统授权(注册中心+网关+认证授权服务(JWT令牌验证)+资源调用服务)

    文章目录 一.OAuth2.0 1.介绍 2.例子 3.执行流程 二.Spring Cloud Security OAuth2 1.环境介绍 2.认证流程 三.整合分布式项目 1.技术方案 2.项目结 ...

  2. SpringCloud整合spring security+ oauth2+Redis实现认证授权

    文章目录 设置通用父工程依赖 构建eureka注册中心 构建认证授权服务 配置文件设置 Security配置类 授权服务配置类 登录实现 测试验证 设置通用父工程依赖 在微服务构建中,我们一般用一个父 ...

  3. Spring Security OAuth2 微服务认证中心自定义授权模式扩展以及常见登录认证场景下的应用实战

    本文源码地址 后端:https://gitee.com/youlaitech/youlai-mall/tree/v2.0.1 前端:https://gitee.com/youlaiorg/mall-a ...

  4. 【Spring Cloud Alibaba 实战 | 总结篇】Spring Cloud Gateway + Spring Security OAuth2 + JWT 实现微服务统一认证授权和鉴权

    一. 前言 hi,大家好~ 好久没更文了,期间主要致力于项目的功能升级和问题修复中,经过一年时间这里只贴出关键部分代码的打磨,[有来]终于迎来v2.0版本,相较于v1.x版本主要完善了OAuth2认证 ...

  5. Spring Security Oauth2 授权码模式下 自定义登录、授权页面

    主要说明:基于若依springcloud微服务框架的2.1版本 嫌弃缩进不舒服的,直接访问我的博客站点: http://binarydance.top//aticle_view.html?aticle ...

  6. Spring Security Oauth2 如何自定义授权获取token

    Oauth2的默认授权模式有四种: 授权码模式-authorization_code 密码模式-password 客户端模式-client_credentials 隐式授权模式-implicit 我们 ...

  7. Spring Security OAuth2 授权码模式 (Authorization Code)

    前言 Spring Security OAuth2 授权码模式 (Authorization Code) 应该是授权登录的一个行业标准 整体流程 首先在平台注册获取CLIENT_ID和CLIENT_S ...

  8. Spring Security+Oauth2四种授权模式

    上一篇文章:Spring Security + OAuth2.0项目搭建:https://blog.csdn.net/qq_42402854/article/details/123057625 接着认 ...

  9. Spring Security OAuth2.0认证授权知识概括

    Spring Security OAuth2.0认证授权知识概括 安全框架基本概念 基于Session的认证方式 Spring Security简介 SpringSecurity详解 分布式系统认证方 ...

最新文章

  1. 在线等:“实习拿到两个不太好的offer,去腾讯还是去阿里?”
  2. JSTL fmt:formatNumber 数字、货币格式化
  3. STL--Lambdas(二)
  4. raptor五个数排序流程图_数据结构与算法(一):排序(上)
  5. delete,drop,truncate 区别
  6. python的git_Pygit: 用Python实现Git的功能
  7. 设计模式- 创建型模式, 建造者模式(2)
  8. 常见报错_【办公】文档打印常见报错,怎么办?
  9. BizTalk Server 2010 - 使用 WCF Service [ 中篇 ]
  10. java实现正态分布累积分布,标准正态分布变量的累积概率分布函数
  11. 中秋节快乐ooooo
  12. UvaLA 4670 Dominating Patterns
  13. Linux HA Cluster的实例演示(2)
  14. iOS Xcode提交IPA时收到苹果邮件ITMS-90338: Non-public API usage
  15. 拉普拉斯矩阵(Laplace Matrix)与瑞利熵(Rayleigh quotient
  16. Springboot Could not resolve placeholder ‘spring.data.mongodb.database’ in value “${spring.data.mong
  17. word怎么根据点画曲线_用word怎么画曲线图 word里如何绘制曲线图
  18. 【学习笔记】树莓派(3B+)及VMware对于代理Proxy的使用
  19. NDM的网络配置文件netmap.cfg
  20. ImageSelector

热门文章

  1. FOC ST MediumFrequencyTask 分析(代码注释)
  2. IAR for ARM 6.5版本扩展新芯片(LPC822)方法
  3. Unity_2D游戏实例从零讲起(2)——手游开场动画的实现
  4. 纯CSS下划线出现动画
  5. remote: Permission to XXX.git denied to quantum6
  6. php sha256withrsa验签,SHA256withRsa的使用
  7. 【优化】Unity游戏加载卡顿原因之一:冗余组件的挂载问题
  8. 【区块链】 区块链哈希算法
  9. RDIFramework.NET(.NET快速信息化系统开发框架) Web版界面样例(可参考)
  10. abstract、 final、 static关键字的使用