security oauth2 password登陆认证相关(1) - 关于grant type

security oauth2 password登陆认证相关(1) - 关于grant type

请求OAuth微服务服务器接口**oauth/login**验证信息返回token,并将token进行缓存持久化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
// AkariServer-oauth: package org.springframework.security.oauth2.provider.endpoint;

/**
* Principal: 用户安全标识,实现此接口(Principal)的类
* @param principal
* @param parameters
* @return
* @throws HttpRequestMethodNotSupportedException
*/
@RequestMapping(value = "/oauth/login", method = RequestMethod.POST)
public ModelAndView postLogin(Principal principal, @RequestParam Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
//验证信息返回token
return MVF.filterData(handleAccessToken(principal, parameters));
}

private OAuth2AccessToken handleAccessToken(Principal principal, Map<String, String> parameters) {
if (!(principal instanceof Authentication)) {
throw new InsufficientAuthenticationException("There is no client authentication. Try adding an appropriate authentication filter.");
}

String clientId = getClientId(principal);
//获取客户端信息
ClientDetails authenticatedClient = getClientDetailsService().loadClientByClientId(clientId);
/**
* TokenRequest:
* 表示在发出的OAuth2令牌请求TokenEndpoint。requestParameters映射应包含原始OAuth2请求中未经修改的原始参数。
* 在隐式流中,AuthorizationEndpoint直接通过令牌请求令牌,在这种情况下 ,令牌AuthorizationRequest会转换为TokenRequest令牌,
* 以通过令牌授权链进行处理。
*/
TokenRequest tokenRequest = getDefaultOAuth2RequestFactory().createTokenRequest(parameters, authenticatedClient);

if (clientId != null && !clientId.equals("")) {
//Only validate the client details if a client authenticated during thi request.
if (!clientId.equals(tokenRequest.getClientId())) {
//double check to make sure that the client ID in the token request is the same as that in the
//authenticated client
throw new InvalidClientException("Given client ID does not match authenticated client");
}
}

if (authenticatedClient != null) {
//确保客户端已请求一组有效的作用域。
oAuth2RequestValidator.validateScope(tokenRequest, authenticatedClient);
}

/**
* grant_type:表示使用的授权模式
* 授权模式不能为空
*/
if (!StringUtils.hasText(tokenRequest.getGrantType())) {
throw new InvalidRequestException("Missing grant type");
}

/**
* implicit不能含有
*/
if (tokenRequest.getGrantType().equals("implicit")) {
throw new InvalidGrantException("Implicit grant type not supported from token endpoint");
}

/**
* 清除tokenRequest的作用域
*/
if (isAuthCodeRequest(parameters)) {
//The scope was requested or determined during the authorization step
if (!tokenRequest.getScope().isEmpty()) {
logger.debug("Clearing scope of incoming token request");
tokenRequest.setScope(Collections.emptySet());
}
}

/**
* 判断是刷新token的请求的话设置令牌请求的作用域
*/
if (isRefreshTokenRequest(parameters)) {
//A refresh token has its own default scopes, so we should ignore any added by the factory here.
tokenRequest.setScope(OAuth2Utils.parseParameterList(parameters.get(OAuth2Utils.SCOPE)));
}

/**
* 授权获取accessToken
* grant type:
* sms: 走到 SmsGranter 执行继承的 统一第三方认证抽象类(包括手机号码认证)access方法
* {@link ru.reimu.akariserver.auth.granter.bean.AbstractThirdPartyGranter.access(ClientDetails client, TokenRequest tokenRequest)}
*
* password: 走到PasswordGranter 执行继承抽象授权者重载的getOAuth2Authentication方法 {@link ru.reimu.akariserver.auth.granter.bean.PasswordGranter}
*/
OAuth2AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);
if (token == null) {
throw new UnsupportedGrantTypeException("Unsupported grant type: " + tokenRequest.getGrantType());
}

return token;
}

重点是这段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
...

/**
* 授权获取accessToken
* grant type:
* sms: 走到 SmsGranter 执行继承的 统一第三方认证抽象类(包括手机号码认证)access方法
* {@link ru.reimu.akariserver.auth.granter.bean.AbstractThirdPartyGranter.access(ClientDetails client, TokenRequest tokenRequest)}
*
* password: 走到PasswordGranter 执行继承抽象授权者重载的getOAuth2Authentication方法 {@link ru.reimu.akariserver.auth.granter.bean.PasswordGranter}
*/
OAuth2AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);

...

执行实现 继承AbstractEndpoint的getTokenGranter()方法实现的TokenGranter接口grant方法,具体gettokenGranter方法可以自己查看源码,先来看看grant实现是在哪个位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/**
* @Author: Tomonori
* @Date: 2019/11/27 16:16
* @Desc: 自定义令牌配置
* 认证服务器逻辑实现
* 让认证服务器AuthorizationServerConfig继承AuthorizationServerConfigurerAdapter,
* 并重写它的configure(ClientDetailsServiceConfigurer clients)方法
*/
@Configuration
@EnableAuthorizationServer
//开启自定义的@Granter注解
@EnableDefinitionGranter("ru.reimu.akariserver.auth.granter")
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

...

@Autowired
private List<TokenGranter> TokenGranters; //授权者

/**
* endpoints配置是在 endpoint包下的EndpointsConfiguration类中
* @param endpoints
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
.tokenStore(redisTokenStore())
.tokenEnhancer(jwtAccessTokenConverter())
.authenticationManager(authenticationManager)
.reuseRefreshTokens(false)
.userDetailsService(userDetailsService)
.tokenServices(defaultTokenServices())
.exceptionTranslator(webResponseExceptionTranslator)
/////////////////////////////////////////////////////////////
//添加授权者
.tokenGranter(new TokenGranter() {
private CompositeTokenGranter delegate;

@Override
public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {
if (this.delegate == null) {
this.delegate = new CompositeTokenGranter(TokenGranters);
}
return this.delegate.grant(grantType, tokenRequest);
}
});
/////////////////////////////////////////////////////////////
}

...

}

可以开到这里我们装配了一个TokenGranter的List的Beans,但是这些TokenGranter又是从哪里注入的呢?

自定义 @Granter 注解

我们将借助ImportBeanDefinitionRegistrar接口实现granter bean的动态注入,即使用@Granter注解的类将在运行时时注入到容器,则上面授权服务器则能自动装配到实现TokenGranter的所有实现类了。

@Granter

1
2
3
4
5
6
7
8
9
10
11
/**
* @Author: Tomonori
* @Date: 2019/12/2 14:33
* @Desc: 标记granter的注解
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Granter {

String granterType();
}

@EnableDefinitionGranter

value值即上面授权服务器的自定义granter包
@EnableDefinitionGranter(“ru.reimu.akariserver.auth.granter”)

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* @Author: Tomonori
* @Date: 2019/12/2 12:32
* @Desc: 开启自定义的@Granter注解
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
//@Import 把用到的资源注册到Bean
@Import({GranterDefinitionBeanRegister.class})
public @interface EnableDefinitionGranter {

String value();
}

动态注入代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/**
* @Author: Tomonori
* @Date: 2019/12/2 14:21
* @Desc: 授权者注册机
*/
public class GranterDefinitionBeanRegister implements ImportBeanDefinitionRegistrar, BeanFactoryAware {

private final Logger log = LoggerFactory.getLogger(this.getClass());

private BeanFactory beanFactory;

@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}

@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
//获取注解的属性
AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(annotationMetadata.getAnnotationAttributes(EnableDefinitionGranter.class.getName()));
String scanPackage = annoAttrs.getString("value");

try {
/**
* loadClassesByAnnotationClass: 传入一个注解类型类,获取使用这个注解的所有的类型类
* Metadata 元数据相关doc: https://www.jianshu.com/p/83725adc2d45
*/
Set<Class<?>> granterSet = ReflectionUtility.loadClassesByAnnotationClass(Granter.class, scanPackage.split(","));

granterSet.forEach(granter -> {
Granter annotation = granter.getAnnotation(Granter.class);
String granterType = annotation.granterType();
//创建bean
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(granter);

beanDefinitionBuilder.addConstructorArgValue(granterType);
//注册bean
beanDefinitionRegistry.registerBeanDefinition(granter.getSimpleName(), beanDefinitionBuilder.getBeanDefinition());
});
} catch (IOException e) {
log.error("Granter register error", e);
} catch (ClassNotFoundException e) {
log.error("Granter register error", e);
}
}
}

选择对应的grant type的tokenGranter

接上面授权服务器,这里实例化了一个CompositeTokenGranter类,我们将执行这个grant方法。下面是代码实现,具体看注释。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
@Override
public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {
if (this.delegate == null) {
this.delegate = new CompositeTokenGranter(TokenGranters);
}
return this.delegate.grant(grantType, tokenRequest);
}

/**
*package org.springframework.security.oauth2.provider.CompositeTokenGranter
*/
public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {
Iterator var3 = this.tokenGranters.iterator();

OAuth2AccessToken grant;
do {
if (!var3.hasNext()) {
return null;
}

TokenGranter granter = (TokenGranter)var3.next();
//注意这里的grant方法将会走实现TokenGranter的一个抽象类下的grant方法
//即AbstractTokenGranter,package org.springframework.security.oauth2.provider.token;
grant = granter.grant(grantType, tokenRequest);
} while(grant == null);

return grant;
}

//package org.springframework.security.oauth2.provider.token.AbstractTokenGranter
public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {
//上面的grant方法中迭代这里判断返回对应的granter实现
if (!this.grantType.equals(grantType)) {
return null;
} else {
String clientId = tokenRequest.getClientId();
ClientDetails client = this.clientDetailsService.loadClientByClientId(clientId);
this.validateGrantType(grantType, client);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Getting access token for: " + clientId);
}

return this.getAccessToken(client, tokenRequest);
}
}

最后调用同包下的this.getAccessToken(ClientDetails client, TokenRequest tokenRequest)方法

1
2
3
protected OAuth2AccessToken getAccessToken(ClientDetails client, TokenRequest tokenRequest) {
return this.tokenServices.createAccessToken(this.getOAuth2Authentication(client, tokenRequest));
}

this.getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest)就将执行我们选择的自定义授权者重载的代码

自定的授权者PasswordGranter类

先来看看里面的代码,到这里先不记录后续

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
/**
* @Author: Tomonori
* @Date: 2020/3/11 15:44
* @Title:
* @Desc: ↓ ↓ ↓ ↓ ↓
* -----
*/
@Granter(granterType = "password")
public class PasswordGranter extends AbstractGranter {

@Autowired
private AuthenticationManager authenticationManager;

protected PasswordGranter(String grantType, AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, RabbitMQSender mqSender) {
super(grantType, tokenServices, clientDetailsService, requestFactory, mqSender);
}

@Override
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
Map<String, String> parameters = new LinkedHashMap<>(tokenRequest.getRequestParameters());
String username = parameters.get("username");
String password = parameters.get("password");
parameters.remove("password");

Authentication userAuth = new UsernamePasswordAuthenticationToken(username, password);
((AbstractAuthenticationToken) userAuth).setDetails(parameters);

try {
/**
* 走到AuthorizationServerConfig的configure(AuthorizationServerEndpointsConfigurer endpoints)方法
* 下的grant(String grantType, TokenRequest tokenRequest) 方法
*
* 然后是 org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.authenticate(Authentication authentication)
*/
userAuth = this.authenticationManager.authenticate(userAuth);
} catch (AccountStatusException var8) {
throw new InvalidGrantException(var8.getMessage());
} catch (BadCredentialsException var9) {
throw new InvalidGrantException(var9.getMessage());
}

if (userAuth != null && userAuth.isAuthenticated()) {
OAuth2Request storedOAuth2Request = this.getRequestFactory().createOAuth2Request(client, tokenRequest);
/**
* return后将会去 {@link org.springframework.security.oauth2.provider.token.AbstractTokenGranter}
* getAccessToken(ClientDetails client, TokenRequest tokenRequest) 重载 {@link ru.reimu.akariserver.auth.config.MyTokenServices}
*/
return new OAuth2Authentication(storedOAuth2Request, userAuth);
} else {
throw new InvalidGrantException("Could not authenticate user: " + username);
}
}

@Override
protected UserDetails access(ClientDetails client, TokenRequest tokenRequest) {
return null;
}
}

未完待续