spring-oauth2 (bearer)是基于spring-security的验证机制,对于第三方访问受限资源时通过token机制来验证
验证steps:

通过时序图来看一下,验证方式:
发送username, password, client_id, client_secret, grant_type到server
server返回包括access_token, token_type, refresh_token, expires_in

其中,expires_in有效期,如果超期了,refresh_token起作用,如下:
使用refresh_token重新发起验证请求

来看一下,如何通过spring配置,完成上面的验证机制:
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:oauth="http://www.springframework.org/schema/security/oauth2"
xmlns:sec="http://www.springframework.org/schema/security" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-1.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<sec:http pattern="/oauth/token" create-session="stateless"
authentication-manager-ref="authenticationManager">
<sec:intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" />
<sec:anonymous enabled="false" />
<sec:http-basic entry-point-ref="clientAuthenticationEntryPoint" />
<sec:custom-filter ref="clientCredentialsTokenEndpointFilter" before="BASIC_AUTH_FILTER" />
<sec:access-denied-handler ref="oauthAccessDeniedHandler" />
</sec:http>
<sec:http pattern="/protected/**" create-session="never"
entry-point-ref="oauthAuthenticationEntryPoint">
<sec:anonymous enabled="false" />
<sec:intercept-url pattern="/protected/**" method="GET" access="IS_AUTHENTICATED_FULLY" />
<sec:custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
<sec:access-denied-handler ref="oauthAccessDeniedHandler" />
</sec:http>
<bean id="oauthAuthenticationEntryPoint"
class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
</bean>
<bean id="clientAuthenticationEntryPoint"
class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
<property name="realmName" value="springsec/client" />
<property name="typeName" value="Basic" />
</bean>
<bean id="oauthAccessDeniedHandler"
class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler">
</bean>
<bean id="clientCredentialsTokenEndpointFilter"
class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
<property name="authenticationManager" ref="authenticationManager" />
</bean>
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider user-service-ref="clientDetailsUserService" />
</sec:authentication-manager>
<bean id="clientDetailsUserService"
class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
<constructor-arg ref="clientDetails" />
</bean>
<bean id="clientDetails" class="it.iol.oauthaaa.security.AAAGuestServiceImpl">
<property name="id" value="mysupplycompany" />
<property name="secretKey" value="mycompanykey" />
</bean>
<sec:authentication-manager id="userAuthenticationManager">
<sec:authentication-provider ref="customUserAuthenticationProvider" />
</sec:authentication-manager>
<bean id="customUserAuthenticationProvider"
class="it.iol.oauthaaa.security.AAAUserAuthenticationProvider">
</bean>
<oauth:authorization-server
client-details-service-ref="clientDetails" token-services-ref="tokenServices">
<oauth:authorization-code />
<oauth:implicit/>
<oauth:refresh-token/>
<oauth:client-credentials />
<oauth:password authentication-manager-ref="userAuthenticationManager"/>
</oauth:authorization-server>
<oauth:resource-server id="resourceServerFilter"
resource-id="springsec" token-services-ref="tokenServices" />
<bean id="tokenStore"
class="org.springframework.security.oauth2.provider.token.InMemoryTokenStore" />
<bean id="tokenServices"
class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
<property name="tokenStore" ref="tokenStore" />
<property name="supportRefreshToken" value="true" />
<property name="accessTokenValiditySeconds" value="120"></property>
<property name="clientDetailsService" ref="clientDetails" />
</bean>
<mvc:annotation-driven />
<mvc:default-servlet-handler />
<context:annotation-config/>
<bean id="MyResource" class="it.iol.oauthaaa.resources.UserResource"></bean>
<bean id="aaaProxy" class="it.iol.oauthaaa.security.AAAProxy"></bean
</beans>
通过发起/oauth/token 请求, filter拦截处理,具体代码:
@Service
public class AAAGuestServiceImpl implements ClientDetailsService {
private String id;
private String secretKey;
@Override
public ClientDetails loadClientByClientId(String clientId)
throws OAuth2Exception {
if (clientId.equals(id))
{
List<String> authorizedGrantTypes = new ArrayList<String>();
authorizedGrantTypes.add("password");
authorizedGrantTypes.add("refresh_token");
authorizedGrantTypes.add("client_credentials");
BaseClientDetails clientDetails = new BaseClientDetails();
clientDetails.setClientId(id);
clientDetails.setClientSecret(secretKey);
clientDetails.setAuthorizedGrantTypes(authorizedGrantTypes);
return clientDetails;
}
else {
throw new NoSuchClientException("No client recognized with id: "
+ clientId);
}
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getSecretKey() {
return secretKey;
}
public void setSecretKey(String secretKey) {
this.secretKey = secretKey;
}
}
现在基于username和passwd来验证(grant_type=password), 在<oauth:authorization-server>的拦截userAuthenticationManager中:
public class AAAUserAuthenticationProvider
implements AuthenticationProvider {
@Autowired
AAAProxy aaaProxy;
@Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
boolean result = aaaProxy.isValidUser(authentication.getPrincipal()
.toString(), authentication.getCredentials().toString());
if (result) {
List<GrantedAuthority> grantedAuthorities =
new ArrayList<GrantedAuthority>();
AAAUserAuthenticationToken auth =
new AAAUserAuthenticationToken(authentication.getPrincipal(),
authentication.getCredentials(), grantedAuthorities);
return auth;
} else {
throw new BadCredentialsException("Bad User Credentials.");
}
}
public boolean supports(Class<?> arg0) {
return true;
}
}
这里的aaproxy是真正来验证user的(代理机制)不细化了,如果用户是有效,做三件事:
1. 在框架的security上下文中更新对象
2. TokenService将会生成一个新的token
3. TokenStore将会保存这个token
然后token就发给request的client了
最后这里是token和resource的代码:
public class AAAUserAuthenticationToken
extends AbstractAuthenticationToken {
private static final long serialVersionUID = -1092219614309982278L;
private final Object principal;
private Object credentials;
public AAAUserAuthenticationToken(Object principal, Object credentials,
Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
this.credentials = credentials;
super.setAuthenticated(true);
}
public Object getCredentials() {
return this.credentials;
}
public Object getPrincipal() {
return this.principal;
}
}
@Path("/userresource")
public class UserResource {
@GET
@Path("/userprofile")
public String getUserProfile(){
return "Welcome in protected Area. User enabled.";
}
}
测试:
使用了Fidder, 可以用curl,wget(what ever you want)
请求:
/OAuthAAA/oauth/token?username=myuser&password=mypassword
&client_id=mysupplycompany&client_secret=mycompanykey&grant_type=password |

资源请求:
/OAuthAAA/protected/userresource/userprofileAuthorization: Bearer 5cf0732b-6bbb-40c7-8fab-dcfefcc2fcfe![]() |
补充:AAProxy的代码:
package it.iol.oauthaaa.security;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.Proxy.Type;
import java.util.List;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
public class AAAProxy {
private Proxy proxy;
private RestTemplate template;
public AAAProxy() {
proxy = new Proxy(Type.HTTP, new InetSocketAddress(
"proxy.abc.net", 3001));
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setProxy(proxy);
template = new RestTemplate(requestFactory);
}
public boolean isValidUser(String user, String password) {
MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
map.add("user", user);
map.add("password", password);
HttpEntity<String> response = template.postForEntity(
"https://authentication.local/auth", map,
String.class);
HttpHeaders headers = response.getHeaders();
List<String> cookies = headers.get("Set-Cookie");
for (String cookie : cookies) {
if (cookie.indexOf("Auth")!=-1)
return true;
}
return false;
}
}
当grant_type="client_credentials"时的验证:

相关资源:
https://raymondhlee.wordpress.com/2014/12/21/implementing-oauth2-with-spring-security/
https://github.com/spring-projects/spring-security-oauth/blob/master/spring-security-oauth2/src/main/java/org/springframework/security/oauth2/provider/authentication/BearerTokenExtractor.java
https://github.com/spring-projects/spring-security-oauth/tree/master/spring-security-oauth2
https://developer.linkedin.com/docs/oauth2
http://api-doc.assembla.com/content/authentication.html
Spring OAuth2 实现

本文介绍如何使用Spring Security实现OAuth2认证流程,包括基于用户名密码的授权方式及客户端凭证授权方式,并提供完整的XML配置示例及关键代码实现。

1037

被折叠的 条评论
为什么被折叠?



