Spring Security OAuth2授权流程

Posted

技术标签:

【中文标题】Spring Security OAuth2授权流程【英文标题】:Spring security OAuth2 authorization process 【发布时间】:2014-05-10 23:14:03 【问题描述】:

谁能告诉我我应该依次调用哪些 http GET 或 POST 方法来授权我的 apache cxf Web 服务并访问资源? 我试着打电话:

http://localhost:8080/oauth/token?client_id=client1&client_secret=client1&grant_type=password&username=client1&password=client1

我能得到的只是令牌响应:

"access_token":"7186f8b2-9bae-48b6-90c2-033a4476c0fc","token_type":"bearer","refresh_token":"d7fe8cda-812b-4b3e-9ce7-b15067e001e4","expires_in":298653

但是在我得到这个令牌之后下一步是什么?如何对用户进行身份验证并访问 url /resources/MyResource/getMyInfo 中的资源,这需要具有角色 ROLE_USER 的用户? 谢谢。

我有以下 servlet 配置:

<http pattern="/oauth/token" create-session="stateless"
      authentication-manager-ref="authenticationManager"
      xmlns="http://www.springframework.org/schema/security">
    <intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY"/>
    <anonymous enabled="false"/>
    <http-basic entry-point-ref="clientAuthenticationEntryPoint"/>
    <custom-filter ref="clientCredentialsTokenEndpointFilter" before="BASIC_AUTH_FILTER"/>
</http>

<http pattern="/resources/**" create-session="never"
      entry-point-ref="oauthAuthenticationEntryPoint"
      xmlns="http://www.springframework.org/schema/security">
    <anonymous enabled="false"/>
    <intercept-url pattern="/resources/MyResource/getMyInfo" access="ROLE_USER" method="GET"/>
    <custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER"/>
    <access-denied-handler ref="oauthAccessDeniedHandler"/>
</http>

<http pattern="/logout" create-session="never"
      entry-point-ref="oauthAuthenticationEntryPoint"
      xmlns="http://www.springframework.org/schema/security">
    <anonymous enabled="false"/>
    <intercept-url pattern="/logout" method="GET"/>
    <sec:logout invalidate-session="true" logout-url="/logout" success-handler-ref="logoutSuccessHandler"/>
    <custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER"/>
    <access-denied-handler ref="oauthAccessDeniedHandler"/>
</http>

<bean id="logoutSuccessHandler" class="demo.oauth2.authentication.security.LogoutImpl">
    <property name="tokenstore" ref="tokenStore"/>
</bean>

<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>

<authentication-manager alias="authenticationManager"
                        xmlns="http://www.springframework.org/schema/security">
    <authentication-provider user-service-ref="clientDetailsUserService"/>
</authentication-manager>

<bean id="clientDetailsUserService"
      class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
    <constructor-arg ref="clientDetails"/>
</bean>

<bean id="clientDetails" class="demo.oauth2.authentication.security.ClientDetailsServiceImpl"/>

<authentication-manager id="userAuthenticationManager"
                        xmlns="http://www.springframework.org/schema/security">
    <authentication-provider ref="customUserAuthenticationProvider">
    </authentication-provider>
</authentication-manager>

<bean id="customUserAuthenticationProvider"
      class="demo.oauth2.authentication.security.CustomUserAuthenticationProvider">
</bean>

<oauth:authorization-server user-approval-handler-ref="userApprovalHandler"
        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="authenticationManager"/>
</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="300000"/>
    <property name="clientDetailsService" ref="clientDetails"/>
</bean>

<bean id="userApprovalHandler"
      class="org.springframework.security.oauth2.provider.approval.TokenServicesUserApprovalHandler">
    <property name="tokenServices" ref="tokenServices" />
</bean>

<bean id="MyResource" class="demo.oauth2.authentication.resources.MyResource"/>

web.xml:

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID"
         version="2.5">
    <display-name>Spring Secure REST</display-name>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring-servlet.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <listener>
        <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
    </listener>
    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <init-param>
            <param-name>contextAttribute</param-name>
            <param-value>org.springframework.web.servlet.FrameworkServlet.CONTEXT.spring</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <servlet>
        <servlet-name>REST Service</servlet-name>
        <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>com.sun.jersey.config.property.packages</param-name>
            <param-value>demo.oauth2.authentication.resources</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>REST Service</servlet-name>
        <url-pattern>/resources/*</url-pattern>
    </servlet-mapping>

    <servlet>
        <servlet-name>spring</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>spring</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

更新: 在这里找到工作示例http://software.aurorasolutions.org/how-to-oauth-2-0-with-spring-security-2/ 可能对有类似问题的人有用

【问题讨论】:

【参考方案1】:

您首先需要创建一个 OAuth2AccessToken,然后您可以使用它来构建一个 OAuth2RestTemplate,然后可以使用它来执行经过身份验证的 GET、POST 调用。

以下是如何设置 OAuth2RestTemplate 的示例:

ResourceOwnerPasswordAccessTokenProvider provider = new ResourceOwnerPasswordAccessTokenProvider();
ResourceOwnerPasswordResourceDetails resource = new ResourceOwnerPasswordResourceDetails();
resource.setClientAuthenticationScheme(AuthenticationScheme.form);
resource.setAccessTokenUri("accessTokenURI");
resource.setClientId("clientId");
resource.setGrantType("password");
resource.setClientSecret("clientSecret");
resource.setUsername("userName");
resource.setPassword("password");
OAuth2AccessToken accessToken = provider.obtainAccessToken(getResource(), new DefaultAccessTokenRequest());
OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(getResource(), new DefaultOAuth2ClientContext(accessToken));

【讨论】:

对不起,我在春季安全方面不是很好。您能否描述完整的过程:我需要在哪里粘贴此代码?如何在 spring-security 上下文 xml 配置中配置它?我应该使用什么 URL 来验证用户身份? 你打算如何访问你的 rest api?如果您有一个 java 客户端应用程序,那么我提供的代码是一个示例,说明如何从您的服务中获取经过身份验证的 OAuth 令牌,它使用它构建一个 OAuthRestTemplate,以便所有后续请求都与它一起传递 OAuth 令牌,该令牌应该允许它访问您受保护的资源。 如果您只想通过浏览器访问您的服务,那么您必须传递从原始请求返回的 oauth 令牌字符串以及您尝试访问的所有后续 URL。用户的身份验证在第一次调用服务以获取 oauth 令牌时完成,如果用户未成功进行身份验证,您将不会在响应中收到访问令牌。 我打算使用 2 个客户端:java spring mvc 和 .net,这就是为什么我需要所有必要的 URL 来发出 http 请求以进行身份​​验证和访问资源。 我在问什么时候应该只使用特定的 /oauth/authorize url?

以上是关于Spring Security OAuth2授权流程的主要内容,如果未能解决你的问题,请参考以下文章

针对授权标头的Spring Security OAuth2 CORS问题

Spring Security OAuth2 - 将参数添加到授权 URL

Spring Security实现OAuth2.0授权服务 - 进阶版

Spring security OAuth2 资源服务器 JWT 授权错误

Spring Security OAuth2.0认证授权

Spring Security OAuth2.0认证授权