分布式服务CAS单点登陆详解及Spring-Security整合CAS实现单点登陆

分布式系统存在诸多子系统,而这些子系统是分别部署在不同的服务器中,那么使用传统方式的session是无法解决的,我们需要使用相关的单点登录技术来解决

1. 什么是单点登陆

单点登录(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一。SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统
分布式服务CAS单点登陆详解及Spring-Security整合CAS实现单点登陆_第1张图片

2. 什么是CAS

CAS 是 Yale 大学发起的一个开源项目,旨在为 Web 应用系统提供一种可靠的单点登录方法,CAS 在 2004 年 12 月正式成为 JA-SIG 的一个项目

CAS 具有以下特点:

  • 开源的企业级单点登录解决方案
  • CAS Server 为需要独立部署的 Web 应用
  • CAS Client 支持非常多的客户端(这里指单点登录系统中的各个 Web 应用),包括 Java, .Net, PHP, Perl, Apache, uPortal, Ruby 等

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

分布式服务CAS单点登陆详解及Spring-Security整合CAS实现单点登陆_第2张图片

SSO单点登录访问流程主要有以下步骤:

  • 访问服务:SSO客户端发送请求访问应用系统提供的服务资源
  • 定向认证:SSO客户端会重定向用户请求到SSO服务器
  • 用户认证:用户身份认证
  • 发放票据:SSO服务器会产生一个随机的Service Ticket
  • 验证票据:SSO服务器验证票据Service Ticket的合法性,验证通过后,允许客户端访问服务
  • 传输用户信息:SSO服务器验证票据通过后,传输用户认证结果信息给客户端

3. CAS单点登陆Demo-第一个系统

  • 首先Maven项目 cas_demo1客户端系统

  • 在cas_demo1 pom文件中添加依赖,并将此项目打包方式改成war包

<packaging>warpackaging>

    <dependencies>
        
        <dependency>
            <groupId>org.jasig.cas.clientgroupId>
            <artifactId>cas-client-coreartifactId>
            <version>3.3.3version>
        dependency>

        <dependency>
            <groupId>javax.servletgroupId>
            <artifactId>servlet-apiartifactId>
            <version>2.5version>
            <scope>providedscope>
        dependency>
    dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.mavengroupId>
                <artifactId>tomcat7-maven-pluginartifactId>
                <version>2.2version>
                <configuration>
                    <port>9001port>
                    <path>/path>
                configuration>

            plugin>
        plugins>
    build>
  • web.xml 配置文件添加以下内容

<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.SingleSignOutHttpSessionListenerlistener-class>
    listener>
    
    <filter>
        <filter-name>CAS Single Sign Out Filterfilter-name>
        <filter-class>org.jasig.cas.client.session.SingleSignOutFilterfilter-class>
    filter>
    <filter-mapping>
        <filter-name>CAS Single Sign Out Filterfilter-name>
        <url-pattern>/*url-pattern>
    filter-mapping>
    
    <filter>
        <filter-name>CASFilterfilter-name>       <filter-class>org.jasig.cas.client.authentication.AuthenticationFilterfilter-class>
        <init-param>
            <param-name>casServerLoginUrlparam-name>
            
            <param-value>http://192.168.25.120:9000/cas/loginparam-value>
        init-param>
        <init-param>
            <param-name>serverNameparam-name>
            
            <param-value>http://localhost:9001param-value>
        init-param>
        
    filter>
    <filter-mapping>
        <filter-name>CASFilterfilter-name>
        <url-pattern>/*url-pattern>
    filter-mapping>
    
    <filter>
        <filter-name>CAS Validation Filterfilter-name>
        <filter-class>     org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilterfilter-class>
        <init-param>
            <param-name>casServerUrlPrefixparam-name>
            <param-value>http://192.168.25.120:9000/casparam-value>
        init-param>
        <init-param>
            <param-name>serverNameparam-name>
            <param-value>http://localhost:9001param-value>
        init-param>
    filter>

    <filter-mapping>
        <filter-name>CAS Validation Filterfilter-name>
        <url-pattern>/*url-pattern>
    filter-mapping>
    
    <filter>
        <filter-name>CAS HttpServletRequest Wrapper Filterfilter-name>
        <filter-class>
            org.jasig.cas.client.util.HttpServletRequestWrapperFilterfilter-class>
    filter>
    <filter-mapping>
        <filter-name>CAS HttpServletRequest Wrapper Filterfilter-name>
        <url-pattern>/*url-pattern>
    filter-mapping>
    
    <filter>
        <filter-name>CAS Assertion Thread Local Filterfilter-name>       <filter-class>org.jasig.cas.client.util.AssertionThreadLocalFilterfilter-class>
    filter>
    <filter-mapping>
        <filter-name>CAS Assertion Thread Local Filterfilter-name>
        <url-pattern>/*url-pattern>
    filter-mapping>
web-app>
  • 创建index.jsp写代码
<%--
  Created by IntelliJ IDEA.
  User: jinsa
  Date: 2020/2/7
  Time: 13:58
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>欢迎使用单点登陆CAS--ONEtitle>
head>
<body>
<h1>欢迎 :  <%=request.getRemoteUser()%> 访问单点登陆系统--ONE ONEh1>
body>
html>


4. CAS单点登陆Demo-第二个系统

  • 创建 cas_demo2系统
  • 只需要把坐标依赖复制过来,改一下本地启动端口
  • 复制webapp文件夹 ,把web.xml配置文件的启动端口改掉即可
  • jsp文件加以区分即可
<%--
  Created by IntelliJ IDEA.
  User: jinsa
  Date: 2020/2/7
  Time: 13:58
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>欢迎使用单点登陆CAS--TWOtitle>
head>
<body>
<h1>欢迎 :  <%=request.getRemoteUser()%> 访问单点登陆系统--TWO TWOh1>
body>
html>


5. 测试是否成功

  • 把两个服务都启动起来,访问本地系统: http://localhost:9001
  • 由于没有登录,所以跳转到CAS登录系统,这里输入用户名密码后访问到demo1系统

分布式服务CAS单点登陆详解及Spring-Security整合CAS实现单点登陆_第3张图片

  • 接下来在访问第二个系统 9002, 此时应该是不需要登录的

在这里插入图片描述

  • 访问流程图

分布式服务CAS单点登陆详解及Spring-Security整合CAS实现单点登陆_第4张图片

6. 单点退出

  • 只需要访问 服务器地址+cas启动端口/cas/logout 即可退出
  • http://192.168.25.120:9000/cas/logout

在这里插入图片描述

7. 配置单点退出后跳转到其他页面

  • 修改cas/WEB-INF系统的配置文件cas-servlet.xml

在这里插入图片描述

  • 修改后测试代码编写,jsp就行
<a href="http://192.168.25.120:9000/cas/logout?service=http://www.baidu.com">单点退出</a>

分布式服务CAS单点登陆详解及Spring-Security整合CAS实现单点登陆_第5张图片

  • 点击后成功跳转到百度 ,说明配置成功

分布式服务CAS单点登陆详解及Spring-Security整合CAS实现单点登陆_第6张图片

8. 配置数据源-登录时查询数据库用户信息

  • 修改cas服务端中web-inf下deployerConfigContext.xml,添加如下配置
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"  
			  p:driverClass="com.mysql.jdbc.Driver"  
			  p:jdbcUrl="jdbc:mysql://localhost:3306/你的数据库名称?characterEncoding=utf8"  
			  p:user="root"  
			  p:password="root" /> 
	<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"/>  
  • 将配置文件中 64 行 key-ref修改成自己的数据源,这里是 dbAuthHandler

分布式服务CAS单点登陆详解及Spring-Security整合CAS实现单点登陆_第7张图片

  • 在 -cas/webapps/cas/WEB-INF/lib/下添加三个jar包,重启即可

在这里插入图片描述

9. 更换CAS默认的登录页面

  • 把你的html页面添加到
  • /home/pyg/passport-cas/tomcat-cas/webapps/cas/WEB-INF/view/jsp/default/ui/目录下
  • 并把casLoginView.jsp 更换成你自己的html页面,注意名字要一样,可以把之前的备份
  • 把你自己页面需要的css js image拷贝到 cas根目录下的目录中

9.1 更改页面内容

  • 我这里把html页面更改为CAS登录页面了,以html为例在你的页面中添加以下内容
<%@ 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" %>
  • 修改你html页面中的form标签 改成之前默认页面的标签 样式用你自己的
# 默认标签 添加你自己的class就行
<form:form method="post" id="fm1" commandName="${commandName}" htmlEscape="true">

<form:errors path="*" id="msg" cssClass="errors" element="div" htmlEscape="false" />

# 结束标签  </form:form>
  • 修改用户名输入框
# 这是CAS默认的 添加你自己的class
<form:input cssClass="required" cssErrorClass="error" id="username" size="25" tabindex="1" accesskey="${userNameAccessKey}" path="username" autocomplete="off" htmlEscape="true" />
  • 修改密码框
# 同上
<form:password cssClass="required" cssErrorClass="error" id="password" size="25" tabindex="2" path="password"  accesskey="${passwordAccessKey}" htmlEscape="true" autocomplete="off" />
  • 修改登录按钮
# 这里的变量是默认的 更改你自己的内容 class
<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" />
  • 重启tomcat测试,我这边显示正常,证明修改成功

10. CAS与SpringSecurity集成

  • pom文件添加依赖
<dependency>  
	   <groupId>org.springframework.securitygroupId>  
	   <artifactId>spring-security-casartifactId>  
	   <version>4.1.0.RELEASEversion>  
dependency>     
<dependency>  
        <groupId>org.jasig.cas.clientgroupId>  
        <artifactId>cas-client-coreartifactId>  
        <version>3.3.3version>  
        <exclusions>  
            <exclusion>  
                <groupId>org.slf4jgroupId>  
                <artifactId>log4j-over-slf4jartifactId>  
            exclusion>  
        exclusions>  
dependency> 
  • 创建spring-security.xml配置文件

<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">
	
	<http pattern="/register.html" security="none">http>
	<http pattern="/css/**" security="none">http>
	<http pattern="/img/**" security="none">http>
	<http pattern="/js/**" security="none">http>
	<http pattern="/plugins/**" security="none">http>
	<http pattern="/data/**" security="none">http>
	<http pattern="/user/register/**" security="none">http>
	<http pattern="/user/sendSms/**" security="none">http>

	
	
	<http use-expressions="false" entry-point-ref="casProcessingFilterEntryPoint">
		<intercept-url pattern="/**" access="ROLE_USER"/>
		<csrf disabled="true"/>
		
		<custom-filter ref="casAuthenticationFilter"  position="CAS_FILTER" />
		<custom-filter ref="requestSingleLogoutFilter" before="LOGOUT_FILTER"/>
		<custom-filter ref="singleLogoutFilter" before="CAS_FILTER"/>
	http>

	
	<beans:bean id="casProcessingFilterEntryPoint" class="org.springframework.security.cas.web.CasAuthenticationEntryPoint">
		
		<beans:property name="loginUrl" value="http://192.168.25.120:9000/cas/login"/>
		<beans:property name="serviceProperties" ref="serviceProperties"/>
	beans:bean>
	<beans:bean id="serviceProperties" class="org.springframework.security.cas.ServiceProperties">
		
		<beans:property name="service" value="http://localhost:8086/login/cas"/>
	beans:bean>
	

	
	<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">
		<beans:property name="authenticationUserDetailsService">
			<beans:bean class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
				<beans:constructor-arg ref="userDetailsService" />
			beans:bean>
		beans:property>
		<beans:property name="serviceProperties" ref="serviceProperties"/>
		
		<beans:property name="ticketValidator">
			<beans:bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
				<beans:constructor-arg index="0" value="http://192.168.25.120:9000/cas"/>
			beans:bean>
		beans:property>
		<beans:property name="key" value="an_id_for_this_auth_provider_only"/>
	beans:bean>

	
	<beans:bean id="userDetailsService" class="自己定义"/>

	
	
	<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://192.168.25.120:9000/cas/logout?service=http://192.168.25.120:9000/cas/login"/>
		<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>
  • 创建认证类
package com.pyg.user.service;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

import java.util.ArrayList;
import java.util.List;

/**
 * @ Description
 * @ auther          宁宁小可爱
 * @ create          2020-02-07 16:20
 */
public class UserDetailServiceImpl implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 创建集合 封装角色权限 查询数据库工作已经交给CAS完成了
        List<GrantedAuthority> list = new ArrayList<>();
        list.add(new SimpleGrantedAuthority("ROLE_USER"));
        return new User(username,"",list);
    }
}

你可能感兴趣的:(java_ssm框架,spring,CAS,单点登陆,Spring-Security)