spring-security-oauth2的项目地址为 https://github.com/spring-projects/spring-security-oauth/tree/master/spring-security-oauth2
spring-security-oauth2的demo 地址为 https://github.com/spring-projects/spring-security-oauth/tree/master/samples/oauth2
为什么要写
1. spring-security-oauth2的demo 不容易让开发者理解, 配置的内容很多, 没有分解的步骤; 我曾经试着按照文档(https://github.com/spring-projects/spring-security-oauth/blob/master/docs/oauth2.md) 配置了几次, 结果全失败, 无一成功(说实话, 这是第二次在实际项目中使用spring & oauth,但还是花了不少时间才完全弄清楚);甚至有时候找错误的原因都不好找.
2.Oauth应该属于security的一部分, 但demo并没有将二者分开, 混在一起
3.总结现在, 方便未来
相比于demo,作了哪些改进
1. 将Spring MVC配置与Oauth的配置分开, 互不影响
2.将用户信息存放数据库
3.将ClientDetails数据存放于数据库,并能对数据进行管理
4.扩展ClientDetails基本属性, 添加trusted属性,用于判断Client是否是可信任的
5.取消掉demo中一些不必要的配置
6.针对不同的资源配置不同的权限
7.token存入数据库而不是内存
开始
>>前提: 使用Maven来管理项目; spring-security-oauth的版本号为 1.0.5.RELEASE
1. 添加Maven dependencies; 以下只列出了主要的
<!--spring security--> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-core</artifactId> <version>${spring.security.version}</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>${spring.security.version}</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-taglibs</artifactId> <version>${spring.security.version}</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-acl</artifactId> <version>${spring.security.version}</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-crypto</artifactId> <version>${spring.security.version}</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>${spring.security.version}</version> </dependency> <dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth2</artifactId> <version>1.0.5.RELEASE</version> </dependency>
2. web.xml配置; 这一步与只使用Spring Security的配置一样.
</pre><pre code_snippet_id="73897" snippet_file_name="blog_20131119_2_2257675" name="code" class="html"> <filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!--contextConfigLocation --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/*.xml</param-value> </context-param> <!-- Spring context listener --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!--hy mvc--> <servlet> <servlet-name>hy</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>2</load-on-startup> </servlet> <servlet-mapping> <servlet-name>hy</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
在classpath创建spring目录, 在该目录里创建 security.xml 文件, 这是所有步骤配置的重点.
3.security.xml的配置; 重点开始.
3.1 起用注解; TokenEndpoint与AuthorizationEndpoint需要
<mvc:annotation-driven/> <mvc:default-servlet-handler/>
1). TokenStore, 使用JdbcTokenStore, 将token信息存放数据库, 需要提供一个dataSource对象; 也可使用InMemoryTokenStore存于内存中
<!--<beans:bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.InMemoryTokenStore"/>--> <beans:bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.JdbcTokenStore"> <beans:constructor-arg index="0" ref="dataSource"/> </beans:bean>
2).TokenServices; 需要注入TokenStore
<beans:bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices"> <beans:property name="tokenStore" ref="tokenStore"/> <beans:property name="supportRefreshToken" value="true"/> </beans:bean>
如果允许刷新token 请将supportRefreshToken 的值设置为true, 默认为不允许
3.3 ClientDetailsService 配置, 使用JdbcClientDetailsService, 也需要提供dataSource, 替换demo中直接配置在配置文件中
<beans:bean id="clientDetailsService" class="org.springframework.security.oauth2.provider.JdbcClientDetailsService"> <beans:constructor-arg index="0" ref="dataSource"/> </beans:bean>
3.4 ClientDetailsUserDetailsService配置, 该类实现了Spring security中 UserDetailsService 接口
<beans:bean id="oauth2ClientDetailsUserService" class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService"> <beans:constructor-arg ref="clientDetailsService"/> </beans:bean>
<beans:bean id="oauth2AuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint"/>
<authentication-manager id="oauth2AuthenticationManager"> <authentication-provider user-service-ref="oauth2ClientDetailsUserService"/> </authentication-manager>
<authentication-manager alias="authenticationManager"> <authentication-provider user-service-ref="userService"> <password-encoder hash="md5"/> </authentication-provider> </authentication-manager>
3.7 OAuth2AccessDeniedHandler配置, 实现AccessDeniedHandler接口
<beans:bean id="oauth2AccessDeniedHandler" class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler"/>
<beans:bean id="oauthUserApprovalHandler" class="org.springframework.security.oauth2.provider.approval.DefaultUserApprovalHandler"> </beans:bean>
<oauth2:authorization-server client-details-service-ref="clientDetailsService" token-services-ref="tokenServices" user-approval-handler-ref="oauthUserApprovalHandler"> <oauth2:authorization-code/> <oauth2:implicit/> <oauth2:refresh-token/> <oauth2:client-credentials/> <oauth2:password/> </oauth2:authorization-server>
<oauth2:refresh-token disabled="true"/>
<beans:bean id="oauth2AccessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased"> <beans:constructor-arg> <beans:list> <beans:bean class="org.springframework.security.oauth2.provider.vote.ScopeVoter"/> <beans:bean class="org.springframework.security.access.vote.RoleVoter"/> <beans:bean class="org.springframework.security.access.vote.AuthenticatedVoter"/> </beans:list> </beans:constructor-arg> </beans:bean>
<!--unity resource server filter--> <oauth2:resource-server id="unityResourceServer" resource-id="unity-resource" token-services-ref="tokenServices"/> <!--mobile resource server filter--> <oauth2:resource-server id="mobileResourceServer" resource-id="mobile-resource" token-services-ref="tokenServices"/>
注意: 每个resource-id的值必须在对应的ClientDetails中resourceIds值中存在
<beans:bean id="clientCredentialsTokenEndpointFilter" class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter"> <beans:property name="authenticationManager" ref="oauth2AuthenticationManager"/> </beans:bean>
<http pattern="/oauth/token" create-session="stateless" authentication-manager-ref="oauth2AuthenticationManager" entry-point-ref="oauth2AuthenticationEntryPoint"> <intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY"/> <anonymous enabled="false"/> <http-basic entry-point-ref="oauth2AuthenticationEntryPoint"/> <custom-filter ref="clientCredentialsTokenEndpointFilter" before="BASIC_AUTH_FILTER"/> <access-denied-handler ref="oauth2AccessDeniedHandler"/> </http>
<!--unity http configuration--> <http pattern="/unity/**" create-session="never" entry-point-ref="oauth2AuthenticationEntryPoint" access-decision-manager-ref="oauth2AccessDecisionManager"> <anonymous enabled="false"/> <intercept-url pattern="/unity/**" access="ROLE_UNITY,SCOPE_READ"/> <custom-filter ref="unityResourceServer" before="PRE_AUTH_FILTER"/> <access-denied-handler ref="oauth2AccessDeniedHandler"/> </http> <!--mobile http configuration--> <http pattern="/m/**" create-session="never" entry-point-ref="oauth2AuthenticationEntryPoint" access-decision-manager-ref="oauth2AccessDecisionManager"> <anonymous enabled="false"/> <intercept-url pattern="/m/**" access="ROLE_MOBILE,SCOPE_READ"/> <custom-filter ref="mobileResourceServer" before="PRE_AUTH_FILTER"/> <access-denied-handler ref="oauth2AccessDeniedHandler"/> </http>注意每一个http对应不同的resourceServer. access-decison-manager-ref对应Oauth的AccessDecisionManager
3.15 默认的http配置,给/oauth/** 设置权限
<http access-denied-page="/login.jsp?authorization_error=2" disable-url-rewriting="true" authentication-manager-ref="authenticationManager"> <intercept-url pattern="/oauth/**" access="ROLE_USER,ROLE_UNITY,ROLE_MOBILE"/> <intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY"/> <form-login authentication-failure-url="/login.jsp?authentication_error=1" default-target-url="/index.jsp" login-page="/login.jsp" login-processing-url="/login.do"/> <logout logout-success-url="/index.jsp" logout-url="/logout.do"/> <anonymous/> </http>
当然,还有些额外的工作你需要做, 如配置dataSource, 创建数据库, 添加用户用户信息, 管理ClientDetails等等.
Oauth相关的数据都是存放在数据库, 我们就可以根据表结果创建domain来实现管理.
为了方便大家学习与讨论, 我现在把该项目放在了GIT OSC上, 访问地址: http://git.oschina.net/shengzhao/spring-oauth-server, 欢迎大家关注.
与文章相关的配置,扩展请访问: http://andaily.com/blog/?cat=19
与文章相关的讨论请访问: http://andaily.com/blog/?page_id=182