Spring Security OAuth2 授权码模式
背景:
由于业务实现中涉及到接入第三方系统(app接入有赞商城等),所以涉及到第三方系统需要获取用户信息(用户手机号、姓名等),为了保证用户信息的安全和接入方式的统一,
采用Oauth2四种模式之一的授权码模式。
介绍:

- 第三方系统调用我方提供的授权接口(步骤1)
- 用户同意授权,后跳转第三方系统(步骤2、3)
- 第三方系统获得code,根据code到我方系统获取token(步骤5、6 )
- 根据获取token访问受保护的资源(步骤8、9)
实际应用中由于合作商户,所以需要直接返回code,不需要用户手动授权,即静默模式,所以需要扩展框架,使其支持自动授权
扩展:
项目使用的是spring-security-oauth2-2.0.15 由于默认情况下,需要用户授权通过才能生成授权码。所以需简要对框架进行扩展
(1)spring-security-oauth2获取code的controller:
RequestMapping(value = "/oauth/authorize")
public ModelAndView authorize(Map<String, Object> model, @RequestParam Map<String, String> parameters,
SessionStatus sessionStatus, Principal principal) { // Pull out the authorization request first, using the OAuth2RequestFactory. All further logic should
// query off of the authorization request instead of referring back to the parameters map. The contents of the
// parameters map will be stored without change in the AuthorizationRequest object once it is created.
AuthorizationRequest authorizationRequest = getOAuth2RequestFactory().createAuthorizationRequest(parameters); Set<String> responseTypes = authorizationRequest.getResponseTypes(); if (!responseTypes.contains("token") && !responseTypes.contains("code")) {
throw new UnsupportedResponseTypeException("Unsupported response types: " + responseTypes);
} if (authorizationRequest.getClientId() == null) {
throw new InvalidClientException("A client id must be provided");
} try { if (!(principal instanceof Authentication) || !((Authentication) principal).isAuthenticated()) {
throw new InsufficientAuthenticationException(
"User must be authenticated with Spring Security before authorization can be completed.");
} ClientDetails client = getClientDetailsService().loadClientByClientId(authorizationRequest.getClientId()); // The resolved redirect URI is either the redirect_uri from the parameters or the one from
// clientDetails. Either way we need to store it on the AuthorizationRequest.
String redirectUriParameter = authorizationRequest.getRequestParameters().get(OAuth2Utils.REDIRECT_URI);
String resolvedRedirect = redirectResolver.resolveRedirect(redirectUriParameter, client);
if (!StringUtils.hasText(resolvedRedirect)) {
throw new RedirectMismatchException(
"A redirectUri must be either supplied or preconfigured in the ClientDetails");
}
authorizationRequest.setRedirectUri(resolvedRedirect); // We intentionally only validate the parameters requested by the client (ignoring any data that may have
// been added to the request by the manager).
oauth2RequestValidator.validateScope(authorizationRequest, client); // Some systems may allow for approval decisions to be remembered or approved by default. Check for
// such logic here, and set the approved flag on the authorization request accordingly.
authorizationRequest = userApprovalHandler.checkForPreApproval(authorizationRequest,
(Authentication) principal);
// TODO: is this call necessary?
boolean approved = userApprovalHandler.isApproved(authorizationRequest, (Authentication) principal);
authorizationRequest.setApproved(approved); // Validation is all done, so we can check for auto approval...
if (authorizationRequest.isApproved()) {
if (responseTypes.contains("token")) {
return getImplicitGrantResponse(authorizationRequest);
}
if (responseTypes.contains("code")) {
return new ModelAndView(getAuthorizationCodeResponse(authorizationRequest,
(Authentication) principal));
}
} // Place auth request into the model so that it is stored in the session
// for approveOrDeny to use. That way we make sure that auth request comes from the session,
// so any auth request parameters passed to approveOrDeny will be ignored and retrieved from the session.
model.put("authorizationRequest", authorizationRequest); return getUserApprovalPageResponse(model, authorizationRequest, (Authentication) principal); }
catch (RuntimeException e) {
sessionStatus.setComplete();
throw e;
} }
52行到59行可知, 当approved 为true的时候会直接返回code码,不会需要用户授权,所以问题变成了如何让扩展的userApprovalHandler生效
(2)扩展的userApprovalHandler生效
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenStore(tokenStore)
.authenticationManager(authenticationManager)
.userDetailsService(authUserDetailService)
.authorizationCodeServices(new JdbcAuthorizationCodeServices(dataSource))
.reuseRefreshTokens(false)
.userApprovalHandler(new AuthApprovalHandler())
.exceptionTranslator(customWebResponseExceptionTranslator)
;
}
9行:AuthotizationServer中增加配置自定义配置
应用:
(1)获取code值
需要APP端定制webview开发,根据/oauth/authrorize路径参数中增加token
请求路径:
参数说明:
|
参数名称
|
类型
|
是否必填
|
描述
|
|---|---|---|---|
response_type |
String | 是 | 固定值“code” |
client_id |
String | 是 | 第三方配置的client_id |
redirect_uri |
String | 是 | 第三方配置的回调地址 |
| state | String | 否 | 第三方自定义使用 |
请求示例:
curl -X POST http://localhost:8421/oauth/authorize -H 'Authorization: Bearer b7c2d63f-edff-4790-add9-0b69df7321b5' -d 'response_type=code&client_id=external&redirect_uri=http://www.baidu.com&state=123'
返回结果: 重定向redirect_uri路径
(2)获取accessToken(有效期暂定72h)
请求参数:
|
参数名称
|
类型
|
是否必填
|
描述
|
|---|---|---|---|
client_id |
String | 是 | 第三方配置的client_id |
client_secret |
String | 是 | 第三方配置的密钥 |
| code | String | 是 | 申请的code |
grant_type |
String | 是 | 固定值“authorization_code” |
redirect_uri |
String | 是 | 第三方配置的回调地址,必须与生成code时的uri一样 |
请求示例:
curl -X POST http://localhost:8421/oauth/token -d 'grant_type=authorization_code&client_id=external&client_secret=D524C1A0811DA49592F841085CC0063EB62B3001252A94542795D1CA9824A941&redirect_uri=http://www.baidu.com&code=4TCYkV'
返回结果:
{"access_token":"95b5be18-49a3-44e1-a527-d5da036cfc3f","token_type":"bearer","refresh_token":"b4488c7d-1e8c-4317-a955-1f4bda013a35","expires_in":9891370,"scope":"auth_base"}
(3) 获取refreshToken
暂时不支持
(4) 访问资源(用户信息)
根据获取到的授权token访问用户资源信息
请求示例:
curl -X POST https://wuxi.test.brightcns.cn/api/v2/user/external/info -H 'Authorization: Bearer e86d752e-8d72-4a33-aa98-8e158ac5b50b'
返回结果:
{"success":true,"msg":"success","code":"SUCCESS","data":{"userId":,"phone":,"nickname":"","avatar":"http://wxcardoss.oss-cn-shanghai.aliyuncs.com/null","realName":null,"extendParam":null}}
Spring Security OAuth2 授权码模式的更多相关文章
- 微信授权就是这个原理,Spring Cloud OAuth2 授权码模式
上一篇文章Spring Cloud OAuth2 实现单点登录介绍了使用 password 模式进行身份认证和单点登录.本篇介绍 Spring Cloud OAuth2 的另外一种授权模式-授权码模式 ...
- 阶段5 3.微服务项目【学成在线】_day16 Spring Security Oauth2_06-SpringSecurityOauth2研究-Oauth2授权码模式-申请令牌
3.3 Oauth2授权码模式 3.3.1 Oauth2授权模式 Oauth2有以下授权模式: 授权码模式(Authorization Code) 隐式授权模式(Implicit) 密码模式(Reso ...
- Spring Security 解析(七) —— Spring Security Oauth2 源码解析
Spring Security 解析(七) -- Spring Security Oauth2 源码解析 在学习Spring Cloud 时,遇到了授权服务oauth 相关内容时,总是一知半解,因 ...
- Spring Security OAuth2 源码分析
Spring Security OAuth2 主要两部分功能:1.生成token,2.验证token,最大概的流程进行了一次梳理 1.Server端生成token (post /oauth/token ...
- Spring Security OAuth2 授权失败(401) 问题整理
Spring Cloud架构中采用Spring Security OAuth2作为权限控制,关于OAuth2详细介绍可以参考 http://www.ruanyifeng.com/blog/2014/0 ...
- Spring Security OAuth2 Demo —— 密码模式(Password)
前情回顾 前几节分享了OAuth2的流程与授权码模式和隐式授权模式两种的Demo,我们了解到授权码模式是OAuth2四种模式流程最复杂模式,复杂程度由大至小:授权码模式 > 隐式授权模式 > ...
- Spring Security OAuth2 Demo —— 客户端模式(ClientCredentials)
前情回顾 前几节分享了OAuth2的流程与其它三种授权模式,这几种授权模式复杂程度由大至小:授权码模式 > 隐式授权模式 > 密码模式 > 客户端模式 本文要讲的是最后一种也是最简单 ...
- 阶段5 3.微服务项目【学成在线】_day16 Spring Security Oauth2_07-SpringSecurityOauth2研究-Oauth2授权码模式-资源服务授权测试
下面要完成 5.6两个步骤 3.3.4 资源服务授权 3.3.4.1 资源服务授权流程 资源服务拥有要访问的受保护资源,客户端携带令牌访问资源服务,如果令牌合法则可成功访问资源服务中的资 源,如下图 ...
- Spring Security OAuth2 微服务认证中心自定义授权模式扩展以及常见登录认证场景下的应用实战
一. 前言 [APP 移动端]Spring Security OAuth2 手机短信验证码模式 [微信小程序]Spring Security OAuth2 微信授权模式 [管理系统]Spring Se ...
随机推荐
- psr-0和psr-4命名规范(未完)
这些命名规范都是为为自动加载服务(避免项目中有很多的include和require) psr-0: 1.命名空间必须与绝对路径一致 2.类名首字母必须大写 3.除入口文件外,其他“.php”必须只有一 ...
- html增加锚点定位
第一种方法,也是最简单的方法是锚点用<a>标签,在href属性中写入DIV的id.如下: <!DOCTYPE html><html><head>< ...
- 检查是否是移动端(Mobile)、ipad、iphone、微信、QQ等
<script type="text/javascript"> //判断访问终端 var browser={ versions:function(){ var u = ...
- angular初体验
所有需要ng管理的代码必须被包裹在一个有ng-app指令的元素中ng-app是ng的入口,表示当前元素的所有指令都会被angular管理(对每一个指令进行分析和操作) 利用angular实现双向绑定: ...
- 坑爹的A标签 href
A标签 href在与click事件同时响应时,如果click事件有提交表单动作,href会阻拦表单提交,解决 1.去掉href 2.href="javascript:void();" ...
- spark读取gz文件
spark 1.5.1是支持直接读取gz格式的压缩包的,和普通文件没有什么区别: 使用spark-shell进入spark shell 交互界面: 输入命令: sc.textFile("\h ...
- 使用模板方法模式简化JDBC操作
在使用JDBC时,会重复的写很多重复的代码,例如 Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; S ...
- Linux tar This does not look like a tar archive
由于昨天公司内网服务器坏了,所以急需搭建新的Linux环境. 在安装maven时,使用tar 命令解压maven.tar.gz出现: tar :This does not look like a ta ...
- 1.thinkphp 权限分析
一.权限分析 用户表 user角色表 role权限表 node think_acces role_id 用户组idnode_id 节点IDlevel 节点等级pid 父级ID THINK_ACCESS ...
- JavaScript中的闭包详解
闭包是JavaScript的重要特性,非常强大,可用于执行复杂的计算,可并不容易理解,尤其是对之前从事面向对象编程的人来说,对 JavaScript 认识和编程显得更难.特别是在看一些开源的JavaS ...