摘要: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=</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.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>

四、当然要新建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获取
五、经过以上的步骤,我们就可以进行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 进行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认证的过程的更多相关文章

  1. 使用Spring Security和OAuth2实现RESTful服务安全认证

    这篇教程是展示如何设置一个OAuth2服务来保护REST资源. 源代码下载github. (https://github.com/iainporter/oauth2-provider)你能下载这个源码 ...

  2. 【Spring Cloud & Alibaba 实战 | 总结篇】Spring Cloud Gateway + Spring Security OAuth2 + JWT 实现微服务统一认证授权和鉴权

    一. 前言 hi,大家好~ 好久没更文了,期间主要致力于项目的功能升级和问题修复中,经过一年时间的打磨,[有来]终于迎来v2.0版本,相较于v1.x版本主要完善了OAuth2认证授权.鉴权的逻辑,结合 ...

  3. 关于 Spring Security OAuth2 中 Feign 调用 Token 问题

    微服务体系中,避免不了服务之间链式调用,一般使用 Feign ,由于使用 Spring Security OAuth2 全局做了安全认证,简单的一种实现方式就是在服务提供方获得 Token 再次通过 ...

  4. springboot+spring security +oauth2.0 demo搭建(password模式)(认证授权端与资源服务端分离的形式)

    项目security_simple(认证授权项目) 1.新建springboot项目 这儿选择springboot版本我选择的是2.0.6 点击finish后完成项目的创建 2.引入maven依赖  ...

  5. Spring Security OAuth2 微服务认证中心自定义授权模式扩展以及常见登录认证场景下的应用实战

    一. 前言 [APP 移动端]Spring Security OAuth2 手机短信验证码模式 [微信小程序]Spring Security OAuth2 微信授权模式 [管理系统]Spring Se ...

  6. Spring Security OAuth2 Demo —— 密码模式(Password)

    前情回顾 前几节分享了OAuth2的流程与授权码模式和隐式授权模式两种的Demo,我们了解到授权码模式是OAuth2四种模式流程最复杂模式,复杂程度由大至小:授权码模式 > 隐式授权模式 > ...

  7. [Spring Cloud实战 | 第六篇:Spring Cloud Gateway+Spring Security OAuth2+JWT实现微服务统一认证授权

    一. 前言 本篇实战案例基于 youlai-mall 项目.项目使用的是当前主流和最新版本的技术和解决方案,自己不会太多华丽的言辞去描述,只希望能勾起大家对编程的一点喜欢.所以有兴趣的朋友可以进入 g ...

  8. 微服务下前后端分离的统一认证授权服务,基于Spring Security OAuth2 + Spring Cloud Gateway实现单点登录

    1.  整体架构 在这种结构中,网关就是一个资源服务器,它负责统一授权(鉴权).路由转发.保护下游微服务. 后端微服务应用完全不用考虑权限问题,也不需要引入spring security依赖,就正常的 ...

  9. 转 - spring security oauth2 password授权模式

    原贴地址: https://segmentfault.com/a/1190000012260914#articleHeader6 序 前面的一篇文章讲了spring security oauth2的c ...

随机推荐

  1. ref和out 传递参数(C#)

    1.参数传递默认都是传递栈空间里面存储的内容 2.如果添加了ref那么传递的都是栈空间地址,而不再是栈空间里面的内容 3.如果添加了out,那么传递的也是栈空间的地址 //写一个方法计算一个int类型 ...

  2. 字符串hash - POJ 3461 Oulipo

    Oulipo Problem's Link ---------------------------------------------------------------------------- M ...

  3. 全新重装win8.1系统后 配置开发及办公环境步骤

    全新重装win8.1系统后 配置开发及办公环境步骤 这两天,系统因配置开发环境出错,重装了一下,为日后方便,故此记录系统配置流程,防日后重装系统计划不周. 安装前,对照步骤,准备好下列安装文件. 0. ...

  4. Oracle基础 各种语句的定义格式

    Oracle内建数据类型 一. 字符数据 1. char(size) 2. varchar2(size) 最常用,最大长度4000字节 3. nvhar(size).nvarchar(size) 4. ...

  5. 【循序渐进学Python】1. Python基础知识

    1. Python安装和配置 首先需要到Python的官方网站(http://www.python.org/getit/) 下载安装包,现在Python的发行版分为兼容之前Python程序的Pytho ...

  6. c#重点[集合类型]异常,数组,集合ArrayList,List<>,hashTable,hashtable泛型(Dictionary)

    1.foreach[对一些数组或集合进行遍历] foreach(类型 变量名 in 集合对象){语句体} //定义一个数组 ,,,,, }; foreach(var i in sNum1) { Con ...

  7. MVC 5 + EF6 入门完整教程14 -- 动态生成面包屑导航

    上篇文章我们完成了 动态生成多级菜单 这个实用组件. 本篇文章我们要开发另一个实用组件:面包屑导航. 面包屑导航(BreadcrumbNavigation)这个概念来自童话故事"汉赛尔和格莱 ...

  8. sql server2008中左连接,右连接,等值连接的区别

    数据库中的连接我了解到有left join,right join,inner join这些,以下是它们的区别: 1)左连接(left join):先取出a表的所有数据,再取出a.b表相匹配的数据 2) ...

  9. linux下安装或升级GCC4.8,以支持C++11标准

    C++11标准在2011年8月份获得一致通过,这是自1998年后C++语言第一次大修订,对C++语言进行了改进和扩充.随后各编译器厂商都各自实现或部分实现了C++中的特性. 如需查看各编译器对C++1 ...

  10. weblogic 12c下jxls导出excel报错Could not initialize class org.apache.poi.xssf.usermodel.XSSFVMLDrawing

    周一,开发反馈weblogic 12c下jxls导出excel报错,公司环境和UAT环境均报错,看日志如下: 2016-06-08 09:16:55,825 ERROR org.jxls.util.T ...