Anwen——Java技术博客,立志成为斜杠青年:IT极客/健身/旅行/国菜大厨

别人的看法都是狗屁,你是谁只有自己说了算。若命运不公,就和他斗到底。
——《哪吒之魔童降世》

目录
单点登录解决方案-CAS
/  

单点登录解决方案-CAS

本文目标

  • 目标1:搭建单点登录服务端,开发单点登录客户端
  • 目标2:实现CAS 认证数据源设置
  • 目标3:更换CAS 登录页面
  • 目标4:掌握CAS与SpringSecurity集成
  • 目标5:完成用户中心单点登录功能

单点登录

  • 单点登录(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一。SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。
  • 我们目前的系统存在诸多子系统,而这些子系统是分别部署在不同的服务器中,那么使用传统方式的session是无法解决的,我们需要使用相关的单点登录技术来解决。

CAS

什么是CAS

  • CAS 是 Yale 大学发起的一个开源项目,旨在为 Web 应用系统提供一种可靠的单点登录方法,CAS 在 2004 年 12 月正式成为 JA-SIG 的一个项目。CAS 具有以下特点:
    • 【1】开源企业级单点登录解决方案
    • 【2】CAS Server 为需要独立部署的 Web 应用
    • 【3】CAS Client 支持非常多的客户端(这里指单点登录系统中的各个 Web 应用),包括 Java, .Net, PHP, Perl, Apache, uPortal, Ruby 等。

server和client

  • 从结构上看,CAS 包含两个部分: CAS Server 和CAS Client。
    • CAS Server :需要独立部署,主要负责对用户的认证工作;
    • CAS Client :负责处理对客户端受保护资源的访问请求,需要登录时,重定向到 CAS Server
  • 下图是CAS 最基本的协议过程:

sso流程

  • SSO单点登录访问流程主要有以下步骤:
  1. 访问服务:SSO客户端发送请求访问应用系统提供的服务资源。
  2. 定向认证:SSO客户端会重定向用户请求到SSO服务器。
  3. 用户认证:用户身份认证。
  4. 发放票据:SSO服务器会产生一个随机的Service Ticket。
  5. 验证票据:SSO服务器验证票据Service Ticket的合法性,验证通过后,允许客户端访问服务。
  6. 传输用户信息:SSO服务器验证票据通过后,传输用户认证结果信息给客户端。

CAS服务端部署

  • Cas服务端其实就是一个war包。在cas-server-4.0.0-release\cas-server-4.0.0\modules目录下cas-server-webapp-4.0.0.war ,将其改名为cas.war放入tomcat目录下的webapps下。启动tomcat自动解压war包。浏览器输入http://localhost:8080/cas/login ,可看到登录页面

我们使用CAS时是无需编码的,一切需要根据我们项目作出调整的需求都可更改相应的配置文件来实现。

登录认证

  • 默认提供的通行证账号为casuser,密码Mellon,按此登录方可登录成功。此通行逻辑源于webapps/cas/WEB-INF/deployerConfigContext.xml文件中的配置:
<bean id="primaryAuthenticationHandler"
      class="org.jasig.cas.authentication.AcceptUsersAuthenticationHandler">
    <property name="users">
        <map>
            <entry key="casuser" value="Mellon"/>
            <entry key="tom" value="123"/>
        </map>
    </property>
</bean>

​ 你可以增加一个entry来增加一个通行证(如我增加了tom)。修改配置文件后别忘了重启tomcat以使其生效。

登出url

  • /logout为登出url,即请求localhost:8080/cas/logout即可注销当前浏览器在CAS中的登录。

端口修改

  • 如果我们不希望用8080端口访问CAS, 可以修改端口,需更改两处配置:

    • 打开tomcat 目录 conf\server.xml  找到下面的配置,更改端口号(如9100)
    <Connector port="9100" protocol="HTTP/1.1"
                   connectionTimeout="20000"
                   redirectPort="8443" />
    
    • 修改CAS配置文件,修改webapps/cas/WEB-INF/cas.properties
    server.name=http://localhost:9100
    

去除https协议

CAS默认使用的是HTTPS协议,如果使用HTTPS协议需要SSL安全证书(需向特定的机构申请和购买) 。如果对安全要求不高或是在开发测试阶段,可使用HTTP协议。我们这里讲解通过修改配置,让CAS使用HTTP协议。

  • 修改cas的``WEB-INF/deployerConfigContext.xml`,找到下面的配置
<!-- Required for proxy ticket mechanism. -->
<bean id="proxyAuthenticationHandler"
      class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler"
      p:httpClient-ref="httpClient" p:requireSecure="false"/>

​ 这里需要增加参数p:requireSecure="false",requireSecure属性意思为是否需要安全验证,即HTTPS,false为不采用

  • 修改cas的/WEB-INF/spring-configuration/ticketGrantingTicketCookieGenerator.xml,找到下面配置:
<bean id="ticketGrantingTicketCookieGenerator" class="org.jasig.cas.web.support.CookieRetrievingCookieGenerator"
      p:cookieSecure="false"
      p:cookieMaxAge="3600"
      p:cookieName="CASTGC"
      p:cookiePath="/cas" />

​ 参数p:cookieSecure="true",同理为HTTPS验证相关,TRUE为采用HTTPS验证,FALSE为不采用https验证。参数p:cookieMaxAge="-1",是cookie的最大生命周期,-1为无生命周期,即只在当前打开的窗口有效,关闭或重新打开其它窗口,仍会要求验证。可以根据需要修改为大于0的数字,比如3600等,意思是在3600秒内,打开任意窗口,都不需要验证。我们这里将cookieSecure改为false , cookieMaxAge 改为3600

  • 修改cas的WEB-INF/spring-configuration/warnCookieGenerator.xml,找到下面配置
<bean id="warnCookieGenerator" class="org.jasig.cas.web.support.CookieRetrievingCookieGenerator"
		p:cookieSecure="false"
		p:cookieMaxAge="3600"
		p:cookieName="CASPRIVACY"
		p:cookiePath="/cas" />

​ 我们这里将cookieSecure改为false , cookieMaxAge 改为3600

CAS客户端入门小Demo

客户端工程1搭建

  1. 搭建工程引入依赖

    创建Maven工程 (war)casclient_demo1 引入cas客户端依赖并指定tomcat运行端口为9001

    <dependencies>
        <!-- cas -->  
        <dependency>  
            <groupId>org.jasig.cas.client</groupId>  
            <artifactId>cas-client-core</artifactId>  
            <version>3.3.3</version>  
        </dependency>  		
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>  
            <scope>provided</scope>
        </dependency>
    </dependencies>  
    <build>  
        <plugins>
            <plugin>  
                <groupId>org.apache.maven.plugins</groupId>  
                <artifactId>maven-compiler-plugin</artifactId>  
                <version>2.3.2</version>  
                <configuration>  
                    <source>1.7</source>  
                    <target>1.7</target>  
                </configuration>  
            </plugin>  
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <configuration>
                    <!-- 指定端口 -->
                    <port>9001</port>
                    <!-- 请求路径 -->
                    <path>/</path>
                </configuration>
            </plugin>
        </plugins>  
    </build>
    
  2. 添加WEB-INF/web.xml,添加配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5">
    
    <!-- 用于单点退出,该过滤器用于实现单点登出功能,可选配置 -->
    <listener>
        <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>
    </listener>
    
    <!-- 该过滤器用于实现单点登出功能,可选配置。 -->
    <filter>
        <filter-name>CAS Single Sign Out Filter</filter-name>
        <filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>CAS Single Sign Out Filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
    <!-- 该过滤器负责用户的认证工作,必须启用它 -->
    <filter>
        <filter-name>CASFilter</filter-name>
        <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>
        <init-param>
            <param-name>casServerLoginUrl</param-name>   
            <param-value>http://localhost:9100/cas/login</param-value>
        </init-param>
        <init-param>
            <param-name>serverName</param-name>
            <!-- cas客户端IP -->
            <param-value>http://localhost:9001</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CASFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
    <!-- 该过滤器负责对Ticket的校验工作,必须启用它 -->
    <filter>
        <filter-name>CAS Validation Filter</filter-name>
        <filter-class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</filter-class>
        <init-param>
            <param-name>casServerUrlPrefix</param-name>
            <param-value>http://localhost:9100/cas</param-value>
        </init-param>
        <init-param>
            <param-name>serverName</param-name>
            <!-- cas客户端IP -->
            <param-value>http://localhost:9001</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CAS Validation Filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
    <!-- 该过滤器负责实现HttpServletRequest请求的包裹, 比如允许开发者通过HttpServletRequest的getRemoteUser()方法获得SSO登录用户的登录名,可选配置。 -->
    <filter>
        <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
        <filter-class>
            org.jasig.cas.client.util.HttpServletRequestWrapperFilter
        </filter-class>
    </filter>
    <filter-mapping>
        <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
    <!-- 该过滤器使得开发者可以通过org.jasig.cas.client.util.AssertionHolder来获取用户的登录名。 比如AssertionHolder.getAssertion().getPrincipal().getName()。 -->
    <filter>
        <filter-name>CAS Assertion Thread Local Filter</filter-name>
        <filter-class>org.jasig.cas.client.util.AssertionThreadLocalFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>CAS Assertion Thread Local Filter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>
  1. 创建index.jsp
<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <title>一品优购</title>
    </head>
    <body>
        欢迎来到一品优购,<%=request.getRemoteUser()%>
    </body>
</html>

request.getRemoteUser()为获取远程登录名

客户端工程2搭建

  • 创建Maven工程 (war)casclient_demo2 引入cas客户端依赖并制定tomcat运行端口为9002
  • 创建web.xml,参照casclient_demo1 ,将serverName(cas客户端IP)的值改为http://localhost:9002,一共两处
  • 创建index.jsp ,内容显示“欢迎来到品优购”

单点登录测试

  • 启动cas服务部署的tomcat(9100)
  • 启动客户端工程1和客户端工程2
  • 地址栏输入http://localhost:9001/http://localhost:9002/ ,地址均会跳转到CAS登录页
  • 输入用户名(casuser)和密码(Mellon)后,页面跳转回9002,再次访问9001也可以打开主页面。

单点登出

欢迎来到一品优购,<%=request.getRemoteUser()%>。
<a href="http://localhost:9100/cas/logout">点击退出</a>

登出到指定url

  • 我们更希望退出登录后,能自动跳转到某个页面,那如何处理呢?

    • 修改cas系统(9100)的配置文件WEB-INF/cas-servlet.xml中的${cas.logout.followServiceRedirects:true}
    <bean id="logoutAction" class="org.jasig.cas.web.flow.LogoutAction"
            p:servicesManager-ref="servicesManager"
            p:followServiceRedirects="${cas.logout.followServiceRedirects:true}"/>
    
  • 这样在cas客户端的登出url中加上回调URL即可

<a href="http://localhost:9100/cas/logout?service=http://www.baidu.com">退出登录</a>

CAS服务端数据源设置

需求分析

  • 我们现在让用户名密码从数据库用户表tb_user里做验证

配置数据源

  • 修改cas服务端中WEB-INFdeployerConfigContext.xml ,在末尾</beans>前添加如下配置
<!-- c3p0数据源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"  
			  p:driverClass="com.mysql.jdbc.Driver"  
			  p:jdbcUrl="jdbc:mysql://127.0.0.1:3306/pinyougoudb?characterEncoding=utf8"  
			  p:user="root"  
			  p:password="123456" /> 
<!-- md5加密 -->
<bean id="passwordEncoder" 
class="org.jasig.cas.authentication.handler.DefaultPasswordEncoder"  
		c:encodingAlgorithm="MD5"  
		p:characterEncoding="UTF-8" />  

<!-- 数据库认证处理器 -->
<bean id="dbAuthHandler"  
		  class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler"  
		  p:dataSource-ref="dataSource"  
		  p:sql="select password from tb_user where username = ?"  
		  p:passwordEncoder-ref="passwordEncoder"/>  
  • 然后在该文件的开头部分找到如下配置:
<bean id="authenticationManager" class="org.jasig.cas.authentication.PolicyBasedAuthenticationManager">
        <constructor-arg>
            <map>
                <entry key-ref="proxyAuthenticationHandler" value-ref="proxyPrincipalResolver" />
                <entry key-ref="primaryAuthenticationHandler" value-ref="primaryPrincipalResolver" />
            </map>
        </constructor-arg>
  • 其中<entry key-ref="primaryAuthenticationHandler" value-ref="primaryPrincipalResolver" />这一配置是使用固定的用户名和密码进行认证。就像我们之前配的tom,123

    <bean id="primaryAuthenticationHandler"
          class="org.jasig.cas.authentication.AcceptUsersAuthenticationHandler">
        <property name="users">
            <map>
                <entry key="casuser" value="Mellon"/>
                <entry key="tom" value="123"/>
            </map>
        </property>
    </bean>
    
  • 要想使用数据库表做动态认证,需要注释此行配置,改为我们添加的数据库认证处理器:

<1-- <entry key-ref="primaryAuthenticationHandler" value-ref="primaryPrincipalResolver" /> -->
<entry key-ref="dbAuthHandler" value-ref="primaryPrincipalResolver"/>

需要注意的是,我们添加的c3p0、md5加密、根据数据源进行认证的dbAuthHandler需要引入3个依赖到WEB-INF/lib

  • c3p0-0.9.1.2.jar
  • cas-server-support-jdbc-4.0.0.jar
  • mysql-connector-java-5.1.32.jar
  • 重启tomcat,使用数据库tb_user表中的数据做登录测试

CAS服务端界面改造

需求分析

  • 我们现在动手将CAS默认的登录页更改为自己项目的登陆页

改头换面

  • 拷贝资源
    • 品优购的登陆页login.html拷贝到cas系统下webapp\cas\WEB-INF\view\jsp\default\ui 目录下
    • css js等文件夹拷贝到 webapp/cas目录下
    • 将原来的casLoginView.jsp 改名为casLoginView-origin.jsp(留作参照),将login.html改名为casLoginView.jsp

修改页面

  • 修改casLoginView.jsp (参照casLoginView-origin.jsp

    • 添加jsp指令
    <%@ page pageEncoding="UTF-8" %>
    <%@ page contentType="text/html; charset=UTF-8" %>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
    <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
    <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
    
    • 修改form标签
    <form:form method="post" id="fm1" commandName="${commandName}" htmlEscape="true" class="sui-form">
    ......
    </form:form>
    
    • 修改用户名框
    <form:input id="username" tabindex="1" 
    	accesskey="${userNameAccessKey}" path="username" autocomplete="off" htmlEscape="true" 
    	placeholder="邮箱/用户名/手机号" class="span2 input-xfat" />
    
    • 修改密码框
    <form:password  id="password" tabindex="2" path="password" 
          accesskey="${passwordAccessKey}" htmlEscape="true" autocomplete="off" 
    	  placeholder="请输入密码" class="span2 input-xfat"   />
    
    • 修改登陆按钮
    <input type="hidden" name="lt" value="${loginTicket}" />
    <input type="hidden" name="execution" value="${flowExecutionKey}" />
    <input type="hidden" name="_eventId" value="submit" />
    <input class="sui-btn btn-block btn-xlarge btn-danger" accesskey="l" value="登陆" type="submit" />
    

错误提示

  • 在表单内加入登录失败提示框
<form:errors path="*" id="msg" cssClass="errors" element="div" htmlEscape="false" />
  • 测试:输入错误的用户名和密码,提示是英文。这个提示信息是在cas\WEB-INF\classes目录下的messages.properties文件中 :

    authenticationFailure.AccountNotFoundException=Invalid credentials.
    authenticationFailure.FailedLoginException=Invalid credentials.
    
  • 设置国际化为zn_CN  ,修改WEB-INF\cas-servlet.xml

    <bean id="localeResolver" 
          class="org.springframework.web.servlet.i18n.CookieLocaleResolver"
          p:defaultLocale="zh_CN" />
    
  • 我们在WEB-INF/classes/messages_zh_CN.properties下增加如下配置(中文转码为UTF-8):

    authenticationFailure.AccountNotFoundException=\u7528\u6237\u4E0D\u5B58\u5728.
    authenticationFailure.FailedLoginException=\u5BC6\u7801\u9519\u8BEF.
    

    第一个是用户名不存在时的错误提示

    第二个是密码错误的提示

CAS客户端与SpringSecurity集成

Spring Security测试工程搭建

  • (1)建立Maven项目casclient_demo3 ,引入spring依赖和spring secrity 相关依赖 ,tomcat端口设置为9003

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring.version}</version>
        </dependency>
    
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>
    
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring.version}</version>
        </dependency>
    
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>4.1.0.RELEASE</version>
        </dependency>
    
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>4.1.0.RELEASE</version>
        </dependency>
    
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>
    
        <!-- cas -->
        <dependency>
            <groupId>org.jasig.cas.client</groupId>
            <artifactId>cas-client-core</artifactId>
            <version>3.3.3</version>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <configuration>
                    <!-- 指定端口 -->
                    <port>9003</port>
                    <!-- 请求路径 -->
                    <path>/</path>
                </configuration>
            </plugin>
        </plugins>
    </build>
    
  • (2)建立web.xml

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring/spring-security.xml</param-value>
    </context-param>
    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>
    
    <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>
    
  • (3)创建配置文件spring-security.xml

    • spring-security.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"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    						http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">
    
    	<!--
          拦截规则
           拦截哪些路径
           该用户需要什么角色才能访问该路径
          use-expressions="false"表示不启用SPEL表达式,如hasRole('ROLE_USER') -->
    	<http use-expressions="false">
    		<!--被授予ROLE_USER角色的用户才能访问根目录及所有其子目录下的资源-->
    		<intercept-url pattern="/**" access="ROLE_ADMIN"></intercept-url>
    		<form-login always-use-default-target="true" default-target-url="/index.html" login-page="/login.html" authentication-failure-url="/login.html" />
    		<csrf disabled="true"/>
    		<headers>
    			<frame-options policy="SAMEORIGIN"/>
    		</headers>
    		<logout/>
    	</http>
    
    	<!-- 认证管理 -->
    	<authentication-manager>
    		<authentication-provider>
    			<user-service>
    				<user name="admin" authorities="ROLE_ADMIN" password="admin"/>
    			</user-service>
    		</authentication-provider>
    	</authentication-manager>
    
    </beans:beans>
    
  • 添加/login.html/index.html测试SpringSecurity搭建是否成功

    • login.html
    <form action="/login" method="post">
        <input type="text" name="username">
        <input type="password" name="password">
        <input type="submit" value="登录">
    </form>
    
    • index.html
    欢迎光临
    

Spring Security与 CAS集成

  • (1)引入CAS客户端、SpringSecurity整合CAS的依赖

    <dependency>  
    	   <groupId>org.springframework.security</groupId>  
    	   <artifactId>spring-security-cas</artifactId>  
    	   <version>4.1.0.RELEASE</version>  
    </dependency>     
    <dependency>  
            <groupId>org.jasig.cas.client</groupId>  
            <artifactId>cas-client-core</artifactId>  
            <version>3.3.3</version>  
            <exclusions>  
                <exclusion>  
                    <groupId>org.slf4j</groupId>  
                    <artifactId>log4j-over-slf4j</artifactId>  
                </exclusion>  
            </exclusions>  
    </dependency> 
    
  • (2)修改spring-security.xml如下(把9001中web.xml中的配置移到了spring配置,并把SpringSecurity认证的提供指定为cas):

    <?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"
                 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                                     http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">
    
        <!--   entry-point-ref  入口点引用 -->
        <http use-expressions="false" entry-point-ref="casProcessingFilterEntryPoint">  
            <intercept-url pattern="/**" access="ROLE_USER"/>   
            <csrf disabled="true"/>  
            <!-- custom-filter为过滤器, position 表示将过滤器放在指定的位置上,before表示放在指定位置之前  ,after表示放在指定的位置之后
        CAS_FILTER为SpringSecurity内置过滤器的别名,详情参加本文末尾附录A -->           
            <custom-filter ref="casAuthenticationFilter"  position="CAS_FILTER" />      
            <custom-filter ref="requestSingleLogoutFilter" before="LOGOUT_FILTER"/>  
            <custom-filter ref="singleLogoutFilter" before="CAS_FILTER"/>  
        </http>
    
    <!-- CAS入口点 开始 -->
        <beans:bean id="casProcessingFilterEntryPoint" class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
    
            <!-- 单点登录服务器登录URL -->  
            <beans:property name="loginUrl" value="http://localhost:9100/cas/login"/>  
            <beans:property name="serviceProperties" ref="serviceProperties"/>  
        </beans:bean>   
    
        <beans:bean id="serviceProperties" class="org.springframework.security.cas.ServiceProperties">  
            <!--service 配置自身工程的根地址+/login/cas,写法固定:根地址+/login/cas  -->  
            <beans:property name="service" value="http://localhost:9003/login/cas"/>
        </beans:bean>  
    <!-- CAS入口点 结束 -->
    
    
    <!-- 认证过滤器 开始 -->
        <beans:bean id="casAuthenticationFilter" class="org.springframework.security.cas.web.CasAuthenticationFilter">  
            <beans:property name="authenticationManager" ref="authenticationManager"/>  
        </beans:bean>  
    
        <!-- 认证管理器 -->
        <authentication-manager alias="authenticationManager">
            <authentication-provider  ref="casAuthenticationProvider">
            </authentication-provider>
        </authentication-manager>
    
        <!-- 认证提供者 -->
        <beans:bean id="casAuthenticationProvider"     class="org.springframework.security.cas.authentication.CasAuthenticationProvider">
    
            <!-- security认证类,这里只用来保存用户明细 -->
            <beans:property name="authenticationUserDetailsService">  
                <beans:bean class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">  
                    <beans:constructor-arg ref="userDetailsService" />  
                </beans:bean>  
            </beans:property>  
    
            <!-- 由cas来做认证 -->
            <beans:property name="serviceProperties" ref="serviceProperties"/>  
    
            <!-- ticketValidator 为票据验证器 -->
            <beans:property name="ticketValidator">  
                <beans:bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">  
                    <beans:constructor-arg index="0" value="http://localhost:9100/cas"/>  
                </beans:bean>  
            </beans:property>  
            <beans:property name="key" value="an_id_for_this_auth_provider_only"/> 
        </beans:bean> 
    
        <!-- 认证类,只是保存用户名,认证还是交由cas来做 -->
        <beans:bean id="userDetailsService" class="top.zhenganwen.demo.service.UserDetailServiceImpl"/>  
    <!-- 认证过滤器 结束 -->
    
    
    <!-- 单点登出  开始  -->     
        <beans:bean id="singleLogoutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter"/>          
        <beans:bean id="requestSingleLogoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">  
    
            <beans:constructor-arg value="http://localhost:9100/cas/logout"/>  
    
            <beans:constructor-arg>  
                <beans:bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/>  
            </beans:constructor-arg>  
    
            <beans:property name="filterProcessesUrl" value="/logout/cas"/>  
    
        </beans:bean>  
    <!-- 单点登出  结束 -->  
    </beans:beans>
    
  • (3)创建UserDetailsServiceImpl 

/**
 * 认证类
 */
public class UserDetailServiceImpl implements UserDetailsService {
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //拿用户名到数据库查询用户的事CAS系统帮你做了
        
        //构建角色集合
		List<GrantedAuthority> authorities=new ArrayList();
        //TODO 应该从数据库查询该用户权限的,这里作为demo简化,直接赋值
		authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
		return new User(username, ""  , authorities);		
	}
}

这个类的主要作用是在登陆后得到用户名,可以根据用户名查询角色或执行一些逻辑。 如通过 SecurityContextHolder获取用户名。

  • (4)访问 localhost:9003 ,页面跳转到CAS服务端登录页面,输入数据库表中的用户登录成功后重定向到 localhost:9003,至此整合成功。

获取登录名

  • 我们在处理后端逻辑需要获得登录名,那么如何获取单点登录的用户名呢? 其实UserDetailServiceImpl已经帮我们保存了登录名,只需建立一个Controller并通过SecurityContextHolder获取返回给前端。

退出登录

  • 要想退出登录(http://localhost:9100/cas/logout)后调往指定页面,则需修改spring-security.xml ,如下面调往http://localhost:9003/index2.html:

    ...
    <beans:bean id="requestSingleLogoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">  
            <beans:constructor-arg value="http://localhost:9100/cas/logout?service=http://localhost:9003/index2.html"/>  
            <beans:constructor-arg>  
                <beans:bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"/>  
            </beans:constructor-arg>  
    
        	<!-- 访问本工程的/logout/cas将单点退出cas系统 -->
            <beans:property name="filterProcessesUrl" value="/logout/cas"/>  
    </beans:bean>
    
  • 在页面上添加链接,退出登录将跳往index2.html

    <a href="/logout/cas">退出登录</a>
    
  • 创建index2.html,将index2.html设置为可匿名访问

    <http pattern="/index2.html" security="none"></http>
    

附录A. Spring Security 内置过滤器表

别名Filter
CHANNEL_FILTERChannelProcessingFilter
SECURITY_CONTEXT_FILTERSecurityContextPersistenceFilter
CONCURRENT_SESSION_FILTERConcurrentSessionFilter
LOGOUT_FILTERLogoutFilter
X509_FILTERX509AuthenticationFilter
PRE_AUTH_FILTERAstractPreAuthenticatedProcessingFilter 的子类
CAS_FILTERCasAuthenticationFilter
FORM_LOGIN_FILTERUsernamePasswordAuthenticationFilter
BASIC_AUTH_FILTERBasicAuthenticationFilter
SERVLET_API_SUPPORT_FILTERSecurityContextHolderAwareRequestFilter
JAAS_API_SUPPORT_FILTERJaasApiIntegrationFilter
REMEMBER_ME_FILTERRememberMeAuthenticationFilter
ANONYMOUS_FILTERAnonymousAuthenticationFilter
SESSION_MANAGEMENT_FILTERSessionManagementFilter
EXCEPTION_TRANSLATION_FILTERExceptionTranslationFilter
FILTER_SECURITY_INTERCEPTORFilterSecurityInterceptor
SWITCH_USER_FILTERSwitchUserFilter


标题:单点登录解决方案-CAS
作者:zanwen
地址:http://www.zhenganwen.top/articles/2018/07/01/1565048875571.html

评论