spring security oauth2 单点登录

本人自己 研究及综合部分网上资料,整理得出,欢迎批评指正,欢迎一起探讨:

1.jar版本:

jdk1.6、spring3.1.1、spring-security3.1.0、spring-security-oauth2-1.0.5

2.背景:

客户公司 已经存在的N个系统,且每个系统用户名会有不一致情况,用户使用系统时需要多个系统协同操作,弊端凸显。为此,需要一个统一认证中心。

3.前提:

3.1:以上N个系统的用户是统一一个出处,如:一个AD域、数据库、LDAP等
3.2:app接入OAuth2的AuthorizationCode方式(其他方式本人未调试过)

扫描二维码关注公众号,回复: 467394 查看本文章

3.3:OAuth的5个表已经生成到中心数据库中,并与中心app注册功能整合在一起。 

4.定义:

4.1AbstractDomain.java:其中guid默认赋值为uuid,该类覆盖equals、hashCode,guid用户标识用户对象

的创建的唯一性,即同一用户在不同客户端登陆后,用户对象不同。
@Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof AbstractDomain)) {
            return false;
        }
        AbstractDomain that = (AbstractDomain) o;
        return guid.equals(that.guid);
    }

    @Override
    public int hashCode() {
        return guid.hashCode();
    }

4.2用户对象User.java:继承AbstractDomain,是中心的用户对象,也是OAuth接口获取用户信息的bean。

5.思路:

        使用cookie缓存用户登录信息,app向中心登录时,获取该cookie,同时验证中心的session中是否存在该用户,存在则执行单点登录。
        认证中心为一个CAS服务,同时需要app向中心注册,并提供appCode及redirectUrl,便于OAuth认证时调用。客户端登陆成功后,中心会向客户端浏览器写入cookie,保存登陆用户信息,作为单点登录凭据,用户关闭浏览器时,cookie自动失效。


6.典型场景:

6.1:用户在浏览器上输入app地址,app过滤器验证未登录后,附上appCode,然后发起OAuth第一步认证请求,中心过滤器判断用户是否登录,

6.1.1未登录:则会跳转到登陆页面,用户填写用户名密码进行登录,中心获取到登录成功后(向客户端写

入cookie),会继续执行OAuth认证,此时会弹出用户授权页面,当用户点击授权按钮后,转6.1.3

6.1.2已登录:转到授权页面(如果之前已经授权,会继续往下走),转6.1.3


6.1.3中心获取到授权后生成授权码,然后跳转到app的redirectUrl,app获取到授权码,根据授权码继续发送认证第二步请求,以授权码换取token,获取到token后,根据token访问中心提供的用户接口获取用户信息(json),app使用用户信息构建app的session、权限等数据,之后做具体业务操作,认证完成。

6.2:用户在该浏览器地址栏再输入app2的地址时,app2也发起OAuth第一步认证,中心仍然跳转到登陆页,此时登陆页获取cookie中用户名与guid信息,自动提交登录信息到action中验证session中是否存在该用户,不存在则调整到登录页面,存在则跳转到app2的redirectUrl,继续向下执行6.1.2操作,以完成认证。


 7.数据流程图:

 

 8.代码:纯干货

 8.1web.xml:

 认证中心过滤器,放过一些请求

 <filter>    
         <filter-name>loginFilter</filter-name>
         <filter-class>*.util.LoginFilter</filter-class>
         <init-param>
   <param-name>AllowRes</param-name>
   <param-value>   
    /login.do;/login.jsp;css;jpg;gif;png;bmp;jpeg;swf;ico;/demo/*;
   /authTypeList.do;/authFail.do; /oauth/authorize;/oauth/token;/oauth/user_info.do;
    isAgainAuth4Client.do
   </param-value>
   </init-param>
    </filter>
    <filter-mapping> 
         <filter-name>loginFilter</filter-name>
         <url-pattern>/*</url-pattern>
    </filter-mapping>

 过滤器代理

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

 额外添加spring session监听,用来处理在线用户

 <listener>
  <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
 </listener>

8.2security.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
             xmlns:beans="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"
             xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
                        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
                        http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-1.0.xsd
                        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">


    <mvc:annotation-driven/>
    <mvc:default-servlet-handler/>

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

    <http auto-config="true" access-denied-page="/ac/login/authFail.do?flag=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 login-processing-url="/login.do" login-page="/ac/portal/login1.jsp" default-target-url="/portal.do"
         authentication-failure-url="/ac/login/authFail.do"/>
        <logout logout-success-url="/ac/portal/login1.jsp?flag=out" logout-url="/logout.do"/>
       
        <anonymous />

  <custom-filter position="CONCURRENT_SESSION_FILTER" ref="concurrencyFilter" />

     <session-management session-authentication-strategy-ref="sessionStrategy"/>
    </http>


    <beans:bean id="clientCredentialsTokenEndpointFilter"
                class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
        <beans:property name="authenticationManager" ref="oauth2AuthenticationManager"/>
    </beans:bean>

    <!--unity resource server filter-->
    <oauth2:resource-server id="unityResourceServer" resource-id="unity-resource" token-services-ref="tokenServices"/>


    <beans:bean id="clientDetailsService" class="com.free.ac.oauth.common.CustomJdbcClientDetailsService">
        <beans:constructor-arg index="0" ref="dataSource"/>
    </beans:bean>


    <!--Config token services-->
    <beans:bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.JdbcTokenStore">
        <beans:constructor-arg index="0" ref="dataSource"/>
    </beans:bean>

    <beans:bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
        <beans:property name="tokenStore" ref="tokenStore"/>
        <beans:property name="clientDetailsService" ref="clientDetailsService"/>
        <beans:property name="supportRefreshToken" value="true"/>
    </beans:bean>


    <beans:bean id="oauthUserApprovalHandler" class="com.free.ac.oauth.common.OauthUserApprovalHandler">
        <beans:property name="tokenServices" ref="tokenServices"/>
        <beans:property name="oauthService" ref="oauthService"/>
    </beans:bean>


    <beans:bean id="jdbcAuthorizationCodeServices" class="org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices">
        <beans:constructor-arg index="0" ref="dataSource"/>
    </beans:bean>


    <oauth2:authorization-server client-details-service-ref="clientDetailsService" token-services-ref="tokenServices"
   user-approval-handler-ref="oauthUserApprovalHandler" user-approval-page="ac/portal/oauth_approval.jsp" error-page="ac/portal/oauth_error.jsp">
        <oauth2:authorization-code authorization-code-services-ref="jdbcAuthorizationCodeServices"/>
        <oauth2:implicit/>
        <oauth2:refresh-token/>
        <oauth2:client-credentials/>
        <oauth2:password/>
    </oauth2:authorization-server>


    <beans:bean id="oauth2AuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint"/>

    <beans:bean id="oauth2ClientDetailsUserService" class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
        <beans:constructor-arg ref="clientDetailsService"/>
    </beans:bean>

    <authentication-manager id="oauth2AuthenticationManager">
        <authentication-provider user-service-ref="oauth2ClientDetailsUserService"/>
    </authentication-manager>

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

    <beans:bean id="oauth2AccessDeniedHandler" class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler"/>


    <authentication-manager alias="authenticationManager">
        <authentication-provider user-service-ref="acUserDetailsService">
            <password-encoder hash="md5"/>
        </authentication-provider>
    </authentication-manager>


 <!-- session管理配置 -->
 <beans:bean id="concurrencyFilter" class="org.springframework.security.web.session.ConcurrentSessionFilter">
  <beans:property name="sessionRegistry" ref="sessionRegistry" />
 </beans:bean>

 <beans:bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl" />
 
 <beans:bean id="sessionStrategy" class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy">
  <beans:property name="maximumSessions" value="1"></beans:property>
  <beans:property name="exceptionIfMaximumExceeded" value="false"></beans:property>
  <beans:constructor-arg name="sessionRegistry" ref="sessionRegistry"></beans:constructor-arg>
 </beans:bean>

</beans:beans>

说明:

oauth2AccessDeniedHandler:自己编写的类,继承TokenServicesUserApprovalHandler,处理是否需要授权。

CustomJdbcClientDetailsService:自定义类,继承JdbcClientDetailsService,处理认证客户端

oauth_approval.jsp:授权成功页

oauth_error.jsp:授权失败页

acUserDetailsService:继承UserDetailsService,是中心提供的用户API接口

猜你喜欢

转载自chun521521.iteye.com/blog/2268953