Spring Security构建Rest服务-1202-Spring Security OAuth开发APP认证框架之重构3种登录方式
SpringSecurityOAuth核心源码解析
蓝色表示接口,绿色表示类

1,TokenEndpoint 整个入口点,相当于一个controller,不同的授权模式获取token的地址都是 /oauth/token ,通过grant_type 参数标识不同的授权类型,这个类就是判断授权类型 grant_type的。
2,TokenEndpoint收到请求后先调用 ClientDetailsService 读取第三方应用的信息。client_id、client_secret 来获取不同的第三方应用信息,封装在ClientDetails里;TokenEndpoint还会创建一个TokenRequest 的对象 ,封装了请求grant_type等信息,将ClientDetails放在TokenRequest 里
3,TokenRequest 会调用TokenGranter (令牌授权者)四种授权模式的实现,根据请求的grant_type 挑一种令牌生成的逻辑,都会产生OAuth2Request(整合了ClientDetails+TokenRequest) 、 Authentication(封装授权用户的信息,通过UserDetailsService获取),这两个对象整合起来生成 OAuth2Authentication 对象(包装了哪个第三方应用在请求哪个用户授权,授权模式,授权参数等信息),
4,OAuth2Authentication 最后传给AuthorizationServerTokenServices(认证服务器令牌服务)的默认实现,生成一个令牌。AuthorizationServerTokenServices 里还有TokenStore用来处理令牌的存取,TokenEnhancer 令牌增强器。
重构三种登录方式
要做的就是在登录成功后,按照Spring Security OAuth 生成 access_token 的逻辑,生成自己的token。
分析:
逆着推,要想有OAuth2AccessToken ------>AuthorizationServerTokenServices(spring已提供默认实现)------>OAuth2Authentication对象--------->Authentication(登录成功后会有这个对象)、OAuth2Request。
所以只需要构建OAuth2Request即可。
OAuth2Request-------->ClientDetails( 封装第三方应用的信息)------------>ClientDetailsService------------>接收ClientId 构建ClientDetails-------------->请求参数
--------->TokenRequest-------->根据ClientDetails信息new出来

此部分代码都是app项目里的

其中一部分是从BasicAuthenticationFilter 类里copy一段代码,这个类是处理Basic登录用的,copy一段获取请求头信息的
ImoocAuthenticationSuccessHandler:
/**
* 认证成功后做的处理
* ClassName: ImoocAuthenticationSuccessHandler
* @Description: 认证成功后做的处理
* @author lihaoyang
* @date 2018年3月1日
*/
@Component("imoocAuthenticationSuccessHandler")
public class ImoocAuthenticationSuccessHandler
//spring默认的登录成功处理器,实现了AuthenticationSuccessHandler接口,适配登录后 重定向和返回json两种用这个实现
extends SavedRequestAwareAuthenticationSuccessHandler
//返回json用这个实现
/*implements AuthenticationSuccessHandler*/{ private Logger logger = LoggerFactory.getLogger(getClass()); //springmvc启动会自动注册一个ObjectMapper
@Autowired
private ObjectMapper objectMapper; @Autowired
private SecurityProperties securityProperties; @Autowired
private ClientDetailsService clientDetailsService; @Autowired
private AuthorizationServerTokenServices authorizationServerTokenServices; @Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException { logger.info("登录成功");
//获取请求头里Authorization信息
String header = request.getHeader("Authorization");
//没有client信息
if (header == null || !header.startsWith("Basic ")) {
throw new UnapprovedClientAuthenticationException("请求头中无client信息");
} /**
* 构造OAuth2Request 第一步,从请求头获取clientId
*/
//base64解码获取clientId、clientSecret
String[] tokens = extractAndDecodeHeader(header, request);
assert tokens.length == 2; String clientId = tokens[0];
String clientSecret = tokens[1];
/**
* 构造OAuth2Request 第二步,根据clientId 获取ClientDetails对象
*/
ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId); if(clientDetails == null){
throw new UnapprovedClientAuthenticationException("clientId对应的配置信息不存在,clientId:"+clientId);
}else if(!StringUtils.equals(clientDetails.getClientSecret(), clientSecret)){
throw new UnapprovedClientAuthenticationException("clientSecret不匹配,clientId:"+clientId);
} /**
* 构造OAuth2Request 第三步,new TokenRequest
* 第一个参数是个map,放的是每个授权模式特有的参数,springsecurity会根据这些参数构建Authentication
* 我们这里已获取到了Authentication,弄个空的map就可
*/
TokenRequest tokenRequest = new TokenRequest(MapUtils.EMPTY_MAP, clientId, clientDetails.getScope(), "custom"); /**
* 构造OAuth2Request 第四步,创建 OAuth2Request
*/
OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails);
/**
* 构造OAuth2Request 第五步,创建 OAuth2Authentication
*/
OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, authentication);
/**
* 构造OAuth2Request 第六步,authorizationServerTokenServices创建 OAuth2AccessToken
*/
OAuth2AccessToken accessToken = authorizationServerTokenServices.createAccessToken(oAuth2Authentication); response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(objectMapper.writeValueAsString(accessToken)); } /**
* base64解码请求头 Basic aW1vb2M6aW1vb2NzZWNyZXQ=
* Decodes the header into a username and password.
*
* @throws BadCredentialsException if the Basic header is not present or is not valid
* Base64
*/
private String[] extractAndDecodeHeader(String header, HttpServletRequest request)
throws IOException {
//Basic aW1vb2M6aW1vb2NzZWNyZXQ= 截取Basic后的
byte[] base64Token = header.substring(6).getBytes("UTF-8");
byte[] decoded;
try {
//解码后格式 用户名:密码
decoded = Base64.decode(base64Token);
}
catch (IllegalArgumentException e) {
throw new BadCredentialsException(
"Failed to decode basic authentication token");
} String token = new String(decoded, "UTF-8"); int delim = token.indexOf(":"); if (delim == -1) {
throw new BadCredentialsException("Invalid basic authentication token");
}
//返回的数组是 [用户名(就是client_id),clientSecret] 其实就是配置的
/**
* security.oauth2.client.clientId = imooc
security.oauth2.client.clientSecret = imoocsecret
*/
return new String[] { token.substring(0, delim), token.substring(delim + 1) };
} }
ImoocResourceServerConfig:安全配置
/**
* 资源服务器,和认证服务器在物理上可以在一起也可以分开
* ClassName: ImoocResourceServerConfig
* @Description: TODO
* @author lihaoyang
* @date 2018年3月13日
*/
@Configuration
@EnableResourceServer
public class ImoocResourceServerConfig extends ResourceServerConfigurerAdapter{ //自定义的登录成功后的处理器
@Autowired
private AuthenticationSuccessHandler imoocAuthenticationSuccessHandler; //自定义的认证失败后的处理器
@Autowired
private AuthenticationFailureHandler imoocAuthenticationFailureHandler; //读取用户配置的登录页配置
@Autowired
private SecurityProperties securityProperties; @Autowired
private SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig; @Override
public void configure(HttpSecurity http) throws Exception { http //----------表单认证相关配置---------------
.formLogin()
.loginPage(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL) //处理用户认证BrowserSecurityController
.loginProcessingUrl(SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_FORM)
.successHandler(imoocAuthenticationSuccessHandler)//自定义的认证后处理器
.failureHandler(imoocAuthenticationFailureHandler) //登录失败后的处理
.and()
//-----------授权相关的配置 ---------------------
.authorizeRequests()
// /authentication/require:处理登录,securityProperties.getBrowser().getLoginPage():用户配置的登录页
.antMatchers(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL,
securityProperties.getBrowser().getLoginPage(),//放过登录页不过滤,否则报错
SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_MOBILE,
SecurityConstants.SESSION_INVALID_PAGE,
SecurityConstants.DEFAULT_VALIDATE_CODE_URL_PREFIX+"/*").permitAll() //验证码
.anyRequest() //任何请求
.authenticated() //都需要身份认证
.and()
.csrf().disable() //关闭csrf防护
.apply(smsCodeAuthenticationSecurityConfig);//把短信验证码配置应用上 } }
启动demo项目,demo项目此时依赖app项目,此时做的是表单登录的处理
访问表单登录:添加Authorization信息,username配置的cilentId:imooc ,password配置的clientSecret:imoocsecret
#第三方应用client_id
security.oauth2.client.clientId = imooc
#注意clientSecret 大写
security.oauth2.client.clientSecret = imoocsecret

响应token:

{
"access_token": "f75ee2e8-a20b-4790-a3b9-64198766dc64",
"token_type": "bearer",
"refresh_token": "013f25fa-affc-445e-bdf6-2adb021aa60d",
"expires_in": 43199
}
拿着token 访问用户信息请求 http://127.0.0.1:8080/user/me,header加上

响应:
{
"password": null,
"username": "user",
"authorities":[
{
"authority": "ROLE_USER"
},
{
"authority": "admin"
}
],
"accountNonExpired": true,
"accountNonLocked": true,
"credentialsNonExpired": true,
"enabled": true,
"userId": "user"
}
至此可以通过通过用户名密码获取token,拿着token访问用户信息了!
具体代码放在了github https://github.com/lhy1234/spring-security
Spring Security构建Rest服务-1202-Spring Security OAuth开发APP认证框架之重构3种登录方式的更多相关文章
- Spring Security构建Rest服务-1300-Spring Security OAuth开发APP认证框架之JWT实现单点登录
基于JWT实现SSO 在淘宝( https://www.taobao.com )上点击登录,已经跳到了 https://login.taobao.com,这是又一个服务器.只要在淘宝登录了,就能直接访 ...
- Spring Security构建Rest服务-1203-Spring Security OAuth开发APP认证框架之短信验证码登录
浏览器模式下验证码存储策略 浏览器模式下,生成的短信验证码或者图形验证码是存在session里的,用户接收到验证码后携带过来做校验. APP模式下验证码存储策略 在app场景下里是没有cookie信息 ...
- Spring Security构建Rest服务-1201-Spring Security OAuth开发APP认证框架之实现服务提供商
实现服务提供商,就是要实现认证服务器.资源服务器. 现在做的都是app的东西,所以在app项目写代码 认证服务器: 新建 ImoocAuthenticationServerConfig 类,@Ena ...
- Spring Security构建Rest服务-1200-SpringSecurity OAuth开发APP认证框架
基于服务器Session的认证方式: 前边说的用户名密码登录.短信登录.第三方登录,都是普通的登录,是基于服务器Session保存用户信息的登录方式.登录信息都是存在服务器的session(服务器的一 ...
- Spring Security构建Rest服务-1205-Spring Security OAuth开发APP认证框架之Token处理
token处理之二使用JWT替换默认的token JWT(Json Web Token) 特点: 1,自包含:jwt token包含有意义的信息 spring security oauth默认生成的t ...
- Spring Security构建Rest服务-1204-Spring Security OAuth开发APP认证框架之Token处理
token处理之一基本参数配置 处理token时间.存储策略,客户端配置等 以前的都是spring security oauth默认的token生成策略,token默认在org.springframe ...
- Spring Cloud构建微服务架构(三)消息总线
注:此文不适合0基础学习者直接阅读,请先完整的将作者关于微服务的博文全部阅读一遍,如果还有疑问,可以再来阅读此文,地址:http://blog.csdn.net/sosfnima/article/d ...
- 构建微服务:Spring boot
构建微服务:Spring boot 在上篇文章构建微服务:Spring boot 提高篇中简单介绍了一下spring data jpa的基础性使用,这篇文章将更加全面的介绍spring data jp ...
- Spring Cloud构建微服务架构(二)服务消费者
Netflix Ribbon is an Inter Process Communication (IPC) cloud library. Ribbon primarily provides clie ...
随机推荐
- 文件权限命令 linux
chmod 777 文件名/文件夹名 拥有所有权限 http://www_xpc8_com/ chmod 755 文件名/文件夹名 属主有所有权限,群组和其他有读.执行 ...
- Dbutils学习(介绍和入门)
一:Dbutils是什么?(当我们很难理解一个东西的官方解释的时候,就让我们记住它的作用) Dbutils:主要是封装了JDBC的代码,简化dao层的操作. 作用:帮助java程序 ...
- ansible-api 调试
使用api的时候有时候会遇到一些难以想象到的错误,可以通过以下几种方式来定位. 1.使用 自定义 callback class ResultCallback(CallbackBase): def v2 ...
- 2) 下载Maven
http://maven.apache.org/ http://maven.apache.org/download.cgi Maven 3.3.3 (Binary tar.gz) Maven 3.3. ...
- Hdu2952 Counting Sheep 2017-01-18 14:56 44人阅读 评论(0) 收藏
Counting Sheep Time Limit : 2000/1000ms (Java/Other) Memory Limit : 32768/32768K (Java/Other) Tota ...
- 安卓添加USB外置UVC摄像头
实现的方法有很多种,按步骤来看适合哪一种,网上说什么接采集卡,其实就是把AV转成UVC,现在市面上很多摄像头直接就已经是UVC的了,在windows上面即插即用. 安卓也是Linux,这个就好办了. ...
- [smarty] 在smarty模板中使用smarty变量初始化 javascript 变量的问题
// 总结:// 1/ 在smarty 模板文件中,使用从php中assign过来的smarty变量,一定需要使用双引号或单引号来括住smarty变量,如:var title="<!- ...
- Android adb获取屏幕分辨率
获取Android设备屏幕分辨率,可以采用最快捷的方式,使用ADB命令获取即可: 打印详细方式: adb shell dumpsys window displays 执行结果: Dump time : ...
- 四、创建覆盖网络--Flannel
Flannel是 CoreOS 团队针对 Kubernetes 设计的一个覆盖网络(Overlay Network)工具,其目的在于帮助每一个使用 Kuberentes 的 CoreOS 主机拥有 ...
- SQL SERVER 2014--内存表实现秒杀场景
===================================== 网上针对“秒杀”的解决方案很多,数据拆分化解热点,READPAST解决锁问题,应用程序排队限制并发等等很多方式,各有优缺点, ...