使用Spring Security Oauth2完成RESTful服务password认证的过程 - 王安琪

标签: spring security oauth2 | 发表时间:2015-07-07 09:29 | 作者:王安琪
出处:
        摘要: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版本)

 
 
一、首先需要使用Spring MVC完成RESTful API的发布,这一步骤的详细情况可见我的另一博文:应用Spring MVC 发布restful服务是怎样的一种体验
二、在/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>
三、在上一步的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.jsc.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.jsc.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>
四、当然要新建com.jsc.dp.UserService类了,其在security.xml文件中配置其为authenticationManager的authentication-provider。com.jsc.dp.UserService类实现自UserDetailsService接口,它需要实现一loadUserByUsername方法,在实现此方法的过程中,又需要新建MyUserDetails类来实现UserDetails接口。实现loadUserByUsername方法时,可以自己依需要从关系数据库、NoSQL或者其它存放用户信息的地方获取。示例代码可以查看之前的博文:使用Spring Security完成RESTful服务用户认证的过程 图1 UserService示例及用户名密码登陆。注意,因为配了access="ROLE_ABCS",因此需要 在拥有相应角色的用户getAuthorities()方法内返回此角色名称:SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority("ROLE_ABCS"); 。
还要新建com.jsc.dp.MyClientDetailsService类,其在security.xml文件中配置其为clientAuthenticationManager的authentication-provider。com.jsc.dp.MyClientDetailsService类实现自ClientDetailsService接口,它需要实现一loadClientByClientId方法,在实现此方法的过程中,又需要新建MyClientDetails类来实现ClientDetails接口。实现loadClientByClientId方法时,可以自己依需要从关系数据库、NoSQL或者其它存放客户端信息的地方获取。示例代码可以查看图1 MyClientDetailsService示例及token获取。注意,这里的getAuthorities()方法对配的access="ROLE_ABCS" 没有影响。
 
 图1 MyClientDetailsService示例及token获取
五、经过以上的步骤,我们就可以进行RESTful服务发布了,发布成功后,需要进行用户认证的试验。
1、如图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 认证成功
 
 
2、我们在认证成功的条件下,使用上面步骤中返回的access_token对  http://localhost:8088/restfulservice/abcs/6?access_token=a7f3e13e-cbb0-417d-a9f8-9764d11db00f 进行GET请求,即可以成功得到返回结果,我是使用浏览器进行HTTP请求的。(具体的逻辑使用Spring MVC的Control完成,见我之前的博文:)。在调试时,可以看到每次请求,进入对应Controller后,代码均会转入UserDetails的String getUsername()方法中。
如果对请求路径里的access_token值稍作修改,如再对  http://localhost:8088/restfulservice/abcs/6?access_token=a7f3e13e-cbb0-417d-a9f8-9764d11db00 8 进行GET请求,则返回不到正确结果,如图5所示,即返回Invalid access token错误。
 
图5 带正确的access_token值请求返回的结果
 
 
 
图6 带不正确的access_token值请求返回的结果
 
 
如果删除access_token,不带access_token值对 http://localhost:8088/restfulservice/abcs/6 进行GET请求时,返回的错误信息如图7所示。
 
图7 不带access_token值请求返回的结果
 
 
这就说明,我们的用户认证配置达到了预期效果。
 

最近有各种之前没有碰到过的问题、技术,有时间整理好分享给大家。




本文链接: 使用Spring Security Oauth2完成RESTful服务password认证的过程,转载请注明。

相关 [spring security oauth2] 推荐:

Spring security oauth2最简单入门环境搭建--二、干货

- - ITeye博客
关于OAuth2的一些简介,见我的上篇blog: http://wwwcomy.iteye.com/blog/2229889 PS:貌似内容太水直接被鹳狸猿干沉. 友情提示 学习曲线:spring+spring mvc+spring security+Oauth2基本姿势,如果前面都没看过请及时关闭本网页.

使用Spring Security Oauth2完成RESTful服务password认证的过程 - 王安琪

- - 博客园_首页
        摘要:Spring Security与Oauth2整合步骤中详细描述了使用过程,但它对于入门者有些重量级,比如将用户信息、ClientDetails、token存入数据库而非内存. 配置过程比较复杂,经过几天时间试验终于成功,下面我将具体的使用Spring Security Oauth2完成password认证的过程记录下来与大家分享.

SPRING BOOT OAUTH2 + KEYCLOAK - service to service call

- - BlogJava-首页技术区
employee-service调用department-service,如果要按OAUTH2.0流程,只需要提供client-id和client-secrect即可. 在KEYCLOAK中引入service-account,即配置该employee-service时,取消standard-flow,同时激活service-account.

cxf + spring 的WS Security示例

- - RSS - IT博客云
WSPasswordCallback的 passwordType属性和 password属性都为null,你只能获得用户名(identifier),一般这里的逻辑是使用这个用户名到数据库中查询其密码,然后再设置到 password属性,WSS4J会自动比较客户端传来的值和你设置的这个值. 你可能会问为什么这里CXF不把客户端提交的密码传入让我们在 ServerPasswordCallbackHandler中比较呢.

Spring Security 实战干货:图解Spring Security中的Servlet过滤器体系

- - SegmentFault 最新的文章
我在 Spring Security 实战干货:内置 Filter 全解析对 Spring Security的内置过滤器进行了罗列,但是 Spring Security真正的过滤器体系才是我们了解它是如何进行"认证"、“授权”、“防止利用漏洞”的关键. Servlet Filter体系. 这里我们以 Servlet Web为讨论目标, Reactive Web暂不讨论.

Spring Boot 使用Spring security 集成CAS - CSDN博客

- -
      创建Maven工程:springboot-security-cas.       创建工程后,打开pom.xml,在pom.xml中加入以下内容:.           .           . 3.创建application.properties.

奔的家园 | CAS 与 Spring Security 3.1整合配置详解

- - Delicious/searchfull
该方式通过获取CAS系统里的角色,来支持CAS与Spring Security的关联,注意该文章authorities部分. 一般来说,Web 应用的安全性包括用户认证(Authentication)和用户授权(Authorization)两个部分. 用户认证指的是验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统.

spring security 3.1.0 控制用户重复登陆

- - CSDN博客架构设计推荐文章
通过配置我们可以实现两个需求 1、限制不允许第二个用户登录,2、第二个登陆用户踢掉前一个登陆用户 . 假设你的spring架构已经可以使用了(其他的主要功能完成),需要增加登录限制功能. 注:这里只写配置不写原理(不懂的就问度娘),其实个人认为先配置好跑起来再研究下原理最好了. 通过sessionRegistry可以获取系统当前在线人数和登录用户信息.

spring security 3中推荐使用BCrypt算法加密密码

- - jackyrong
spring security 3中推荐使用BCrypt算法加密密码了,以前使用的是md5,. Md5PasswordEncoder 和 ShaPasswordEncoder,现在不推荐了,推荐用bcrpt. Bcrpt中的salt可以是随机的,比如:.   其中strenth为长度. 已有 0 人发表留言,猛击->> 这里<<-参与讨论.

Spring Security判断用户是否已经登录 - 简书

- -
方法一、JSP中检查user principal. 需要:.