前言
OAuth2.0定义的几种Authorization Grant
Authorization Grant是资源所有者赋予第三方client的一份证书,第三方client可以凭此证书获取一个access token,后者可以用来直接访问资源所有者的某些受限资源,而不用知道资源所有者的用户名密码等信息。
OAuth2.0中目前有四种授权类型:Authorization Code,implicit,resource owner password credentials,client credentials
实现
Spring的具体实现(TokenGranter)
上述几种类型的授权类型中,Spring有对应的几种TokenGranter,类继承结构如下图:
可以看到类CompositeTokenGranter和AbstractTokenGranter直接implements接口TokenGranter。其中,
前者实际类定义中维持了一个对所有具体TokenGranter的引用list,调用其grant方法时,会遍历该list,调用每一个引用的grant方法,直到有granter生成并返回OAuth2AccessToken或者list到最后。该TokenGranter用到的地方自己看代码的过程中,发现在TokenEndpoint中有对其的直接引用,实际结合对authorization-server的配置就可以直到这个CompositeTokenGranter对其他TokenGranter的引用list怎么来的了:
<oauth2:authorization-server client-details-service-ref="clientDetailsService" token-services-ref="tokenServices"
user-approval-handler-ref="oauthUserApprovalHandler"
user-approval-page="oauth_approval"
error-page="oauth_error">
<!--后面这几种授权方式应该都会整合进入CompositeTokenGranter的List<TokenGranter>属性当中的-->
<oauth2:authorization-code authorization-code-services-ref="authorizationCodeServices" />
<oauth2:implicit/>
<oauth2:refresh-token/>
<oauth2:client-credentials/>
<oauth2:password/>
</oauth2:authorization-server>
而后者AbstractTokenGranter则是个抽象类,有具体实现的TokenGranter会继承该类
接口TokenGranter用于生成OAuth2AccessToken,接口就定义了一个方法:OAuth2AccessToken grant(String grantType, AuthorizationRequest authorizationRequest)。OAuth2AccessToken定义如下,其getValue()方法返回的即是返回客户端的access_token。
public interface OAuth2AccessToken {
public static String BEARER_TYPE = "Bearer";
public static String OAUTH2_TYPE = "OAuth2";
/**
* The access token issued by the authorization server. This value is REQUIRED.
*/
public static String ACCESS_TOKEN = "access_token";
/**
* The type of the token issued as described in <a
* href="http://tools.ietf.org/html/draft-ietf-oauth-v2-22#section-7.1">Section 7.1</a>. Value is case insensitive.
* This value is REQUIRED.
*/
public static String TOKEN_TYPE = "token_type";
/**
* The lifetime in seconds of the access token. For example, the value "3600" denotes that the access token will
* expire in one hour from the time the response was generated. This value is OPTIONAL.
*/
public static String EXPIRES_IN = "expires_in";
/**
* The refresh token which can be used to obtain new access tokens using the same authorization grant as described
* in <a href="http://tools.ietf.org/html/draft-ietf-oauth-v2-22#section-6">Section 6</a>. This value is OPTIONAL.
*/
public static String REFRESH_TOKEN = "refresh_token";
/**
* The scope of the access token as described by <a
* href="http://tools.ietf.org/html/draft-ietf-oauth-v2-22#section-3.3">Section 3.3</a>
*/
public static String SCOPE = "scope";
/**
* The additionalInformation map is used by the token serializers to export any fields used by extensions of OAuth.
* @return a map from the field name in the serialized token to the value to be exported. The default serializers
* make use of Jackson's automatic JSON mapping for Java objects (for the Token Endpoint flows) or implicitly call
* .toString() on the "value" object (for the implicit flow) as part of the serialization process.
*/
Map<String, Object> getAdditionalInformation();
Set<String> getScope();
OAuth2RefreshToken getRefreshToken();
String getTokenType();
boolean isExpired();
Date getExpiration();
int getExpiresIn();
String getValue();
}
- AuthorizationCodeTokenGranter
我们的项目使用的授权类型是Authorization Code,其余授权类型暂时完全没看的状态。
结合上面说的,第三方client会先申请authorization code(这部分逻辑以后单独说),得到code之后,由第三方client向/auth/token发起获取access token请求(调用的实际就是TokenEndpoint中的逻辑),类似:http://localhost:8080/oauth/token?client_id=id&client_secret=secret&grant_type=authorization_code&scope=read,write&redirect_uri=none&code=3iW8lA
@RequestMapping(value = "/oauth/token")
public class TokenEndpoint extends AbstractEndpoint {
@RequestMapping
public ResponseEntity<OAuth2AccessToken> getAccessToken(Principal principal,
@RequestParam(value = "grant_type", required = false) String grantType,
@RequestParam Map<String, String> parameters) {
//...
//...
//部分代码省略 getTokenGranter()返回的是CompositeTokenGranter对象
OAuth2AccessToken token = getTokenGranter().grant(grantType, authorizationRequest);
if (token == null) {
throw new UnsupportedGrantTypeException("Unsupported grant type: " + grantType);
}
return getResponse(token);
}
}
而CompositeTokenGranter的实现如下:
public class CompositeTokenGranter implements TokenGranter {
private final List<TokenGranter> tokenGranters;
public CompositeTokenGranter(List<TokenGranter> tokenGranters) {
this.tokenGranters = new ArrayList<TokenGranter>(tokenGranters);
}
public OAuth2AccessToken grant(String grantType, AuthorizationRequest authorizationRequest) {
for (TokenGranter granter : tokenGranters) {
OAuth2AccessToken grant = granter.grant(grantType, authorizationRequest);
if (grant!=null) {
return grant;
}
}
return null;
}
}
所以,这样,如果我们上面的xml配置中,启用了AuthorizaitonCode授权方式,那么这里就会使用到,并调用其grant方法获取access token(grant方法实际是AbstractTokenGranter类中定义好的,子类只需要定义好其需要的几个方法即可,比如getOAuth2Authentication())
in class AbstractTokenGranter:
public OAuth2AccessToken grant(String grantType, AuthorizationRequest authorizationRequest) {
if (!this.grantType.equals(grantType)) {
return null;
}
String clientId = authorizationRequest.getClientId();
ClientDetails client = clientDetailsService.loadClientByClientId(clientId);
validateGrantType(grantType, client);
logger.debug("Getting access token for: " + clientId);
return getAccessToken(authorizationRequest);
}
protected OAuth2AccessToken getAccessToken(AuthorizationRequest authorizationRequest) {
DefaultAuthorizationRequest outgoingRequest = new DefaultAuthorizationRequest(authorizationRequest);
outgoingRequest.setApproved(true);
// FIXME: do we need to explicitly set approved flag here?
return tokenServices.createAccessToken(getOAuth2Authentication(outgoingRequest));
}
in subclass AuthorizationCodeTokenGranter(会对client id和redirect uri等进行校验,并且要注意的是,因为是使用code换取access token,所以这里还需要另用掉的code失效):
@Override
protected OAuth2Authentication getOAuth2Authentication(AuthorizationRequest authorizationRequest) {
Map<String, String> parameters = authorizationRequest.getAuthorizationParameters();
String authorizationCode = parameters.get("code");
String redirectUri = parameters.get(AuthorizationRequest.REDIRECT_URI);
if (authorizationCode == null) {
throw new OAuth2Exception("An authorization code must be supplied.");
}
AuthorizationRequestHolder storedAuth = authorizationCodeServices.consumeAuthorizationCode(authorizationCode);//让code失效,如果使用DB的话,就是删除该code对应的记录,这里DB删除之后会把这条记录同时返回
if (storedAuth == null) {
throw new InvalidGrantException("Invalid authorization code: " + authorizationCode);
}
AuthorizationRequest pendingAuthorizationRequest = storedAuth.getAuthenticationRequest();//反序列化,client申请code时是将code及其对应的AuthorizationRequest对象序列化之后一并存入Db的,所以这里可以反序列化出来使用
// https://jira.springsource.org/browse/SECOAUTH-333
// This might be null, if the authorization was done without the redirect_uri parameter
String redirectUriApprovalParameter = pendingAuthorizationRequest.getAuthorizationParameters().get(
AuthorizationRequest.REDIRECT_URI);
if ((redirectUri != null || redirectUriApprovalParameter != null)
&& !pendingAuthorizationRequest.getRedirectUri().equals(redirectUri)) {
throw new RedirectMismatchException("Redirect URI mismatch.");
}
String pendingClientId = pendingAuthorizationRequest.getClientId();
String clientId = authorizationRequest.getClientId();
if (clientId != null && !clientId.equals(pendingClientId)) {
// just a sanity check.
throw new InvalidClientException("Client ID mismatch");
}
// Secret is not required in the authorization request, so it won't be available
// in the pendingAuthorizationRequest. We do want to check that a secret is provided
// in the token request, but that happens elsewhere.
Map<String, String> combinedParameters = new HashMap<String, String>(storedAuth.getAuthenticationRequest()
.getAuthorizationParameters());
// Combine the parameters adding the new ones last so they override if there are any clashes
combinedParameters.putAll(parameters);
// Similarly scopes are not required in the token request, so we don't make a comparison here, just
// enforce validity through the AuthorizationRequestFactory.
DefaultAuthorizationRequest outgoingRequest = new DefaultAuthorizationRequest(pendingAuthorizationRequest);
outgoingRequest.setAuthorizationParameters(combinedParameters);
Authentication userAuth = storedAuth.getUserAuthentication();
return new OAuth2Authentication(outgoingRequest, userAuth);
}
之后剩余的就是生成token的流程了。源码里面都写的很清晰。
自己记住的就几点:
- token的value是一个UUID值,而存储进入DB的是对这个UUID值处理之后的值(类似MD5),所以想直接根据返回给client的value直接查询DB是有点麻烦的==,=
- 貌似authorization id也是类似的生成逻辑....
- 在现在项目oauth相关的表存储中,很多都用到了序列化将整个对象存储为blob类型的字段,还涉及到反序列化
- 单步调试的过程中,发现,一次申请access token的请求中,多次调用了根据clientId查询DB的情况,所以在实际使用中,考虑使用缓存。目前我们自己对查询client和查询user信息加了缓存,token的缓存略麻烦,暂时没加。
- 大小: 161.8 KB
分享到:
相关推荐
Spring Security OAuth2.0学习笔记 ...OAuth2.0认证的四种模式?它们的大体流程是什么? Spring cloud Security OAuth2包括哪些组件?职责? 分布式系统认证需要解决的问题? 掌握学习方法,掌握思考方式。
Spring boot+Spring Security Oauth2.0,Sprint cloud+Spring Security Oauth2集成。四种认证方式。附带有代码,和案例,案例,还有视频链接。我保证看完就回,如果视频链接失效,评论回复我,我单独再给你一份。
spring security + oauth 2.0 实现单点登录、认证授权,直接贴代码
spring security 基于oauth 2.0 实现 sso 单点登录Demo 使用 spring security 基于oauth 2.0 实现 sso 单点登录Demo spring boot + spring security + spring security oauth
spring security oauth2.0 需要的基础 sql 文件
通过点击viewbutton获取用户openid,实现方式oauth2.0认证
视频配套笔记_Spring Security OAuth2.0认证授权_v1.1 完整详细 pdf无障碍阅读,代码完整可复制
Spring Security框架 oAuth2.0协议标准,实现认证服务器和资源服务器,并实现oAuth2.0自定义登陆和授权界面,Access_token和Refresh_token过期时间的设置,以及数据库表结构建表语句,参考博客能更好的学习和了解,...
分布式系统的认证和授权 分布式架构采用 Spring Cloud Alibaba 认证和授权采用 Spring Security OAuth2.0 实现方法级权限控制 网关采用 gateway 中间件 服务注册和发现采用 nacos
Spring-security-oauth2.0实现
SpringSecurity OAuth2.0用户认证
本篇文章主要介绍了Spring Security Oauth2.0 实现短信验证码登录示例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
基于springcloud+security+oauth2.0实现的权限管理系统.zip
全网最新的Cloud 权限系统 基于Spring Boot 2.0.4.RELEASE ...基于 Spring Security oAuth 深度定制,支持社交登录等 完整的OAuth 2.0 流程,资源服务器控制权限 去除了部分对于开发不友好的中间件,快速上手
OAuth2.0协议是当前开放流行的认证协议,本课程使用流行的Spring Security认证框架及OAuth2.0协议实现单体及分布式系统的认证授权技术解决方案。
基于Spring Cloud、OAuth2.0的前后端分离的系统。 通用RBAC权限设计及其数据权限和分库分表 支持服务限流、动态路由、灰度发布、 支持常见登录方式, 多系统SSO登录 基于 Spring Cloud Hoxton 、Spring Boot 2.3、 ...
Spring Security OAuth2.0
Spring Security OAuth 2.0
Enhance your application’s security no matter the platform with OAuth 2.0 Leverage OAuth 2.0 to protect your APIs and to access and secure your application’s data A recipe-based guide that will ...
Spring Boot 2.0.8.RELEASE 、Spring Cloud Finchley.SR2 、Spring Security OAuth2 1. 减少中间件依赖 2.0 依赖中间件只需要 mysql、redis 即可,提供傻瓜式部署方案,大大缩减了上手和使用成本。 2. 提供常见...