spring-oauth2原理及使用(REST)

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

验证steps:



 
通过时序图来看一下,验证方式:

 

发送username, password, client_id, client_secret, grant_typeserver

 

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/userprofile
Authorization: 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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值