摘要:Spring Security与Oauth2整合步骤中详细描述了使用过程
,但它对于入门者有些重量级,好比将用户信息、ClientDetails、token存入数据库而非内存
。配置过程比较复杂,通过几天时间试验终于成功,下面我将具体的使用
Spring Security Oauth2完成password认证的过程记录下来与你们分享。
关键字: HTTP Authentication, rest, spring security
, spring mvc
前提:IntelliJ IDEA (
13.1.5 版本),
apache maven
(
3.2.3 版本),
Tomcat(7.0.56版本), Spring(3.2.4版本),
spring-security-oauth2(2.0.7
版本
)
2、在/webapp/WEB-INF/web.xml文件中添加相应的filter:
org.springframework.web.filter.DelegatingFilterProxy
,
及
mapping
,具体如如下所示。
<?xml version=
"1.0"
encoding=
"UTF-8"
?>
<web-app xmlns=
"http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi=
"http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=
"http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version=
"3.1"
>
<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>
<context-
param
>
<
param
-
name
>contextConfigLocation</
param
-
name
>
<
param
-
value
>
/WEB-INF/security.xml
</
param
-
value
>
</context-
param
>
<listener>
<listener-
class
>org.springframework.web.context.ContextLoaderListener</listener-
class
>
</listener>
<servlet>
<servlet-
name
>restful</servlet-
name
>
<servlet-
class
>org.springframework.web.servlet.DispatcherServlet</servlet-
class
>
<load-on-startup>
1
</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-
name
>restful</servlet-
name
>
<
url
-pattern>/</
url
-pattern>
</servlet-mapping>
</web-app>
3、在上一步的web.xml文件中能够看到,Spring须要加载
/WEB-INF/下的security.xml文件,所以咱们在
/WEB-INF/下建立
security.xml文件,其主要内容以下所示。这里比
使用Spring Security完成RESTful服务用户认证中的
security.xml文件配置要复杂得多(见我以前的博文:
使用Spring Security完成RESTful服务用户认证的过程
)。注意,这里的配置文件中的
<security:http pattern=
"/abcs/**"
>
<security:intercept-
url
pattern=
"/abcs/**"
access=
"ROLE_ABCS"
/>
access必须指定一个角色名称,使用
use-expressions=
"true"
isAuthenticated()
这里是不容许的。所以,咱们须要在
实现UserDetails接口、实现
getAuthorities()方法时返回
此角色名称,在拥有此角色的用户认证经过后,才能够访问/abcs/**资源。
<?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:oauth2=
"http://www.springframework.org/schema/security/oauth2"
xmlns:mvc=
"http://www.springframework.org/schema/mvc"
xmlns:security=
"http://www.springframework.org/schema/security"
xsi:schemaLocation=
"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2.xsd"
>
<mvc:annotation-driven/>
<mvc:default-servlet-handler/>
<bean
id
=
"tokenStore"
class
=
"org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore"
/>
<bean
id
=
"tokenServices"
class
=
"org.springframework.security.oauth2.provider.token.DefaultTokenServices"
>
<property
name
=
"tokenStore"
ref=
"tokenStore"
/>
<property
name
=
"supportRefreshToken"
value
=
"true"
/>
<!--<property name="clientDetailsService" ref="clientDetailsService"/>-->
</bean>
<bean
id
=
"clinetAuthenticationEntryPoint"
class
=
"org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint"
/>
<bean
id
=
"accessDeniedHandler"
class
=
"org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler"
/>
<bean
id
=
"userApprovalHandler"
class
=
"org.springframework.security.oauth2.provider.approval.DefaultUserApprovalHandler"
/>
<!--client-->
<bean
id
=
"clientDetailsService"
class
=
"com.anqi.dp.controllers.MyClientDetailsService"
/>
<bean
id
=
"clientDetailsUserDetailsService"
class
=
"org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService"
>
<constructor-arg ref=
"clientDetailsService"
/>
</bean>
<bean
id
=
"clientCredentialsTokenEndpointFilter"
class
=
"org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter"
>
<property
name
=
"authenticationManager"
ref=
"clientAuthenticationManager"
/>
</bean>
<security:authentication-manager
id
=
"clientAuthenticationManager"
>
<security:authentication-provider user-service-ref=
"clientDetailsUserDetailsService"
/>
</security:authentication-manager>
<oauth2:authorization-server client-details-service-ref=
"clientDetailsService"
token-services-ref=
"tokenServices"
user-approval-handler-ref=
"userApprovalHandler"
>
<oauth2:authorization-
code
/>
<oauth2:implicit/>
<oauth2:refresh-token/>
<oauth2:client-credentials/>
<oauth2:password/>
</oauth2:authorization-server>
<security:http pattern=
"/oauth/token"
create-session=
"stateless"
authentication-manager-ref=
"clientAuthenticationManager"
>
<security:anonymous
enabled
=
"false"
/>
<security:http-basic entry-point-ref=
"clinetAuthenticationEntryPoint"
/>
<security:custom-filter ref=
"clientCredentialsTokenEndpointFilter"
before=
"BASIC_AUTH_FILTER"
/>
<security:access-denied-handler ref=
"accessDeniedHandler"
/>
</security:http>
<!--client-->
<!--user-->
<bean
id
=
"userService"
class
=
"com.anqi.dp.controllers.UserService"
/>
<security:authentication-manager alias=
"authenticationManager"
>
<security:authentication-provider user-service-ref=
"userService"
>
<!--<security:password-encoder hash="md5"/>-->
</security:authentication-provider>
</security:authentication-manager>
<!--user-->
<oauth2:resource-server
id
=
"mobileResourceServer"
resource-
id
=
"mobile-resource"
token-services-ref=
"tokenServices"
/>
<bean
id
=
"accessDecisionManager"
class
=
"org.springframework.security.access.vote.UnanimousBased"
>
<constructor-arg>
<list>
<bean
class
=
"org.springframework.security.oauth2.provider.vote.ScopeVoter"
/>
<bean
class
=
"org.springframework.security.access.vote.RoleVoter"
/>
<bean
class
=
"org.springframework.security.access.vote.AuthenticatedVoter"
/>
</list>
</constructor-arg>
</bean>
<security:http pattern=
"/abcs/**"
create-session=
"never"
entry-point-ref=
"clinetAuthenticationEntryPoint"
access-decision-manager-ref=
"accessDecisionManager"
>
<security:anonymous
enabled
=
"false"
/>
<security:intercept-
url
pattern=
"/abcs/**"
access=
"ROLE_ABCS"
/>
<security:custom-filter ref=
"mobileResourceServer"
before=
"PRE_AUTH_FILTER"
/>
<security:access-denied-handler ref=
"accessDeniedHandler"
/>
</security:http>
</beans>
4、固然要新建com.anqi.dp.UserService类了,其在
security.xml文件中配置其为
authenticationManager
的
authentication-provider。
com.anqi.dp.UserService类实现自UserDetailsService接口,它须要实现一loadUserByUsername方法,在实现此方法的过程当中,又须要新建MyUserDetails类来实现UserDetails接口。实现
loadUserByUsername方法时,能够本身依须要从关系数据库、NoSQL
或者其它存放用户信息的地方获取。示例代码能够查看
以前的博文:
使用Spring Security完成RESTful服务用户认证的过程
图1 UserService示例及用户名密码登录
。注意,由于配了
access=
"ROLE_ABCS"
,所以须要
在拥有相应角色的用户
getAuthorities()方法内返回
此角色名称:
SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority("ROLE_ABCS"); 。
还要新建com.anqi.dp.MyClientDetailsService类,其
在
security.xml文件中配置其为
clientAuthenticationManager
的
authentication-provider。
com.anqi.dp.MyClientDetailsService类实现自
ClientDetailsService接口
,它须要实现一loadClientByClientId方法,在实现此方法的过程当中,又须要新建MyClientDetails类来实现ClientDetails接口。实现
loadClientByClientId
方法时,能够本身依须要从关系数据库、NoSQL
或者其它存放客户端信息的地方获取。示例代码能够查看
图1
MyClientDetailsService
示例及token获取。注意,
这里的
getAuthorities()方法对
配的
access=
"ROLE_ABCS"
没有影响。
图1
MyClientDetailsService
示例及token获取
5、通过以上的步骤,咱们就能够进行RESTful服务发布了,发布成功后,须要进行用户认证的试验。
一、如图1
MyClientDetailsService
示例及token获取
所示,咱们使用REST Client工具对
http://127.0.0.1:8088/restfulservice/oauth/token
路径发出POST请求,其中须要在Request Parameters中添加client_id、client_secret、grant_type与user_name、password键值对。
如此,
即进行了模拟的经过用户名密码获取token的过程
。图2中是client认证失败时的Response,我将client_secret更改后的结果。图3是user认证失败时的
Response,我将password更改后的结果。
图4是认证成功时的
Response。能够看出,在认证成功时的
Response中存在access_token字段,这就是咱们获取到的token
。
图2 client认证失败
图3 user认证失败
图4 认证成功
图5
带正确的access_token值请求返回的结果
图6 带不正确的access_token值请求返回的结果
图7 不带
access_token值请求返回的结果
这就说明,咱们的用户认证配置达到了预期效果。
最近有各类以前没有碰到过的问题、技术,有时间整理好分享给你们。
html