前言
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和OAuth 2.0是两个在Web应用安全领域广泛应用的框架,它们结合使用可以构建强大的单点登录(SSO)和认证授权系统。在这个系统中,`xp-sso-server`代表了认证服务器,而`xp-sso-client-a`和`xp-sso-...
本讲义结合代码将深入探讨如何利用Spring Security OAuth2.0 实现这一目标。 首先,OAuth2.0 有四个核心角色:资源所有者(Resource Owner)、客户端(Client)、资源服务器(Resource Server)和授权服务器...
Spring Security OAuth2.0学习笔记 ...OAuth2.0认证的四种模式?它们的大体流程是什么? Spring cloud Security OAuth2包括哪些组件?职责? 分布式系统认证需要解决的问题? 掌握学习方法,掌握思考方式。
在Spring Security OAuth2.0的配置中,开发者需要定义这些组件的行为,例如设置授权类型、定义客户端信息、配置资源服务器等。同时,还需要处理各种授权流,如授权码流、密码流、客户端凭据流等,每种流都有其特定的...
Spring boot+Spring Security Oauth2.0,Sprint cloud+Spring Security Oauth2集成。四种认证方式。附带有代码,和案例,案例,还有视频链接。我保证看完就回,如果视频链接失效,评论回复我,我单独再给你一份。
- 使用 Spring Security OAuth2 框架,它可以简化 OAuth 2.0 的实现。首先,你需要在项目中引入相关依赖。 - 配置授权服务器,定义客户端详情,包括客户端ID、客户端秘密、授权类型等。 - 创建数据库表,用于存储...
微信OAuth2.0授权是一种广泛应用于移动应用和网站的第三方登录解决方案,主要目的是为了安全地获取用户的微信身份标识——openid,以便提供个性化服务或者与其他微信功能集成。在本文中,我们将详细探讨微信OAuth2.0...
spring security 基于oauth 2.0 实现 sso 单点登录Demo 使用 spring security 基于oauth 2.0 实现 sso 单点登录Demo spring boot + spring security + spring security oauth
Spring Security OAuth 2.0 是一种基于 OAuth 2.0 协议的身份验证和授权框架,它提供了一个灵活和可扩展的解决方案来保护基于 Web 的应用程序。OAuth 2.0 是一种行业标准的授权协议,提供了多种授权模式来满足不同的...
spring security oauth2.0 需要的基础 sql 文件
这个"spring oauth2.0 例子"是一个在MyEclipse集成开发环境中创建的示例项目,用于演示如何在Spring框架中实现OAuth2.0认证和授权流程。 首先,OAuth2.0的核心概念包括客户端、资源所有者(用户)、授权服务器和...
1. **授权码模式**:这是OAuth2.0中最常见的授权类型,适用于有服务器端的应用。当用户同意授权后,授权服务器会返回一个授权码给客户端。客户端随后使用这个授权码向授权服务器验证并换取访问令牌(Access Token)...
在Java中实现OAuth 2.0,我们可以利用Spring Security OAuth2库,它提供了服务端(Authorization Server)和客户端(Resource Server)的支持。在提供的压缩包中,包含两个Maven项目,一个是`oauthserver`,代表了...
在这个OAuth2.0实现中,它将定义项目依赖,如Spring Security OAuth2库,用于处理OAuth2.0协议的各个步骤。可能的依赖包括`spring-security-oauth2`, `spring-web`, `spring-security-core`, `spring-security-...
OAuth2.0主要有四种授权类型:授权码(Authorization Code)、隐式(Implicit)、密码(Resource Owner Password Credentials)和客户端凭证(Client Credentials)。这里主要讨论最常用的授权码流程: 1. 用户在...
分布式系统的认证和授权 分布式架构采用 Spring Cloud Alibaba 认证和授权采用 Spring Security OAuth2.0 实现方法级权限控制 网关采用 gateway 中间件 服务注册和发现采用 nacos
在"spring-security-oauth2-example-master"这个项目中,你可以找到一个完整的SpringBoot集成OAuth2.0的示例,包括配置、控制器、服务以及客户端的实现,通过学习和研究该项目,可以更好地理解上述知识点,并实际...
在本项目中,通过集成Spring Security OAuth2,我们可以实现用户的认证和授权流程。用户可以通过微信等第三方账号登录,简化注册和登录步骤,提升用户体验。同时,OAuth2.0的令牌(Token)机制确保了数据传输的安全...
Spring Security OAuth2.0 提供了实现OAuth2.0规范的组件,与Spring Security整合,为开发人员提供了便捷的方式来实现安全认证和授权。 **1. OAuth2.0 架构组件** OAuth2.0 主要包含四个角色:资源所有者(Resource...
Spring Security框架 oAuth2.0协议标准,实现认证服务器和资源服务器,并实现oAuth2.0自定义登陆和授权界面,Access_token和Refresh_token过期时间的设置,以及数据库表结构建表语句,参考博客能更好的学习和了解,...