使用CAS实现单点登录功能

Posted mumuyinxin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用CAS实现单点登录功能相关的知识,希望对你有一定的参考价值。

单点登录

简介

  • 单点登录(Single Sign On),简称 SSO
  • 简单来说,就是只要一次登录了某个子系统,就顺带登录了其他的子系统。
  • 其目的很简单,就是为了减少用户访问子系统的成本。

CAS服务器部署

上传tomcat服务器压缩到文件夹/usr/local/cas目录下,解压,修改tomcat文件夹名为tomcat

mkdir /usr/local/cas
cd /usr/local/cas

修改tomcat配置文件的端口号

?

<server port="8010" shutdown="SHUTDOWN">

<Connector port="9100" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />

关闭tomcat服务器,把CAS的war包放入tomcat的webapps目录当中后再启动tomcat服务器 对war包时行解压

关闭tomcat服务器后, 删除war包

/usr/local/cas/tomcat/bin/shutdown.sh
rm -rf /usr/local/cas/tomcat/webapps/cas.war

监听启动

tail -f /usr/local/cas/tomcat/logs/catalina.out

启动tomcat服务器

/usr/local/cas/tomcat/bin/startup.sh

访问http://192.168.1.88:9100/cas/login

输入用户名casuser 密码:Mellon

CAS使用

去除https认证与设置cookie

?
?

//关闭tomcat
/usr/local/cas/tomcat/bin/shutdown.sh

//修改参数
vi /usr/local/cas/tomcat/webapps/cas/WEB-INF/classes/services/HTTPSandIMAPS-10000001.json
{
    "@class" : "org.apereo.cas.services.RegexRegisteredservice",
    "serviceId" : "^(https|http|imaps)://.*",
    "name" : "HTTPS and IMAPS",
    "id" : 10000001,
    "description" : "This service definition authorizes all application urls that support HTTPS and IMAPS protocols.",
    "evaluationorder" : 10000
}



//添加参数
vi /usr/local/cas/tomcat/webapps/cas/WEB-INF/classes/application.properties
cas.tgc.secure=false
cas.serviceRegistry.initFromJson=true
#配置允许登出后跳转到指定页面
cas.logout.followServiceRedirects=true
#跳转到指定页面需要的参数名为 service
cas.logout.redirectParameter=service


//重启tomcat
/usr/local/cas/tomcat/bin/startup.sh

用户名和密码

数据源方法

修改mysql远程访问权限

?

create user user_cas identified with mysql_native_password by '123456';
grant all on *.* to 'user_cas'@'%';
FLUSH PRIVILEGES;

配置用户认证

将用户认证配置cas.authn.accept.users=casuser::Mellon注释掉
vi /usr/local/cas/tomcat/webapps/cas/WEB-INF/classes/application.properties
#cas.authn.accept.users=casuser::Mellon
#设置用户认证配置(根据实际情况配置数据源)
cas.authn.jdbc.query[0].url=jdbc:mysql://192.168.1.2:3306/fmstore?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&useSSL=false
#根据实际情况配置
cas.authn.jdbc.query[0].user=user_cas
#根据实际情况配置
cas.authn.jdbc.query[0].password=123456
cas.authn.jdbc.query[0].sql=select * from tb_user where username= ?
cas.authn.jdbc.query[0].driverClass=com.mysql.cj.jdbc.Driver
cas.authn.jdbc.query[0].fieldPassword=password
cas.authn.jdbc.query[0].fieldExpired=expired
#cas.authn.jdbc.query[0].passwordEncoder.encodingAlgorithm=MD5

自定义登录页面

添加主题名称

  • 在/usr/local/cas/tomcat/webapps/cas/WEB-INF/classes/service/HTTPSandIMAPS-10000001.json中添加"theme" : "mypage"

放入静态资源文件

  • 把图片放到static/images当中
  • js放到static/js当中
  • css放到static/css当中

创建mypage.properties文件

? 在/usr/local/cas/tomcat/webapps/cas/WEB-INF/classes目录当中创建mypage.properties文件

#原cas默认的css样式,如果更改了,某些页面样式将丢失

cas.standard.css.file=/css/cas.css
#自己的样式
cas.page.login.css=/css/pages-login.css
cas.webbase.css=/css/webbase.css

cas.jquery.easing.min.js=/js/jquery.easing.min.js
cas.jquery.min.js=/js/jquery.min.js
cas.jquery.placeholder.min.js=/js/jquery.placeholder.min.js
cas.login.js=/js/login.js
cas.sui.min.js=/js/sui.min.js

cas.qq.png = /images/img/qq.png
cas.weixin.png = /images/img/weixin.png

在/usr/local/cas/tomcat/webapps/cas/WEB-INF/classes/templates中创建文件夹mypage

把页面放进去

在application.properties当中添加主题

  • cas.theme.defaultThemeName=mypage

用户服务添加CAS

引入pom文件

<!--spring-security-->
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-taglibs</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-cas</artifactId>
</dependency>
<dependency>
    <groupId>org.jasig.cas.client</groupId>
    <artifactId>cas-client-core</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.slf4j</groupId>
            <artifactId>log4j-over-slf4j</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<!-- https://mvnrepository.com/artifact/joda-time/joda-time -->
<dependency>
    <groupId>joda-time</groupId>
    <artifactId>joda-time</artifactId>
    <version>2.10</version>
</dependency>

在web.xml当中添加过滤器

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

添加配置文件spring/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">

    <!-- 放行静态资源-->
    <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="/register.html" security="none"></http>
    <http pattern="/user/add.do" security="none"></http>
    <http pattern="/user/sendCode.do" security="none"></http>

    <!--   entry-point-ref  入口点引用 -->
    <http use-expressions="false" entry-point-ref="casProcessingFilterEntryPoint">
        <!--配置拦截器, 拦截所有请求, 应该具有ROLE_USER的权限才可以访问我们系统-->
        <intercept-url pattern="/**" access="ROLE_USER"/>
        <csrf disabled="true"/>
        <!-- custom-filter为过滤器,
                position 表示将过滤器放在指定的位置上,
                before表示放在指定位置之前,
                after表示放在指定的位置之后
        -->
        <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://192.168.1.88:9100/cas/login"/>
        <beans:property name="serviceProperties" ref="serviceProperties"/>
    </beans:bean>
    <beans:bean id="serviceProperties" class="org.springframework.security.cas.ServiceProperties">
        <!--service 配置自身工程的根地址+/login/cas   -->
        <beans:property name="service" value="http://127.0.0.1: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"/>
        <!-- ticketValidator 为票据验证器 -->
        <beans:property name="ticketValidator">
            <beans:bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
                <beans:constructor-arg index="0" value="http://192.168.1.88:9100/cas"/>
            </beans:bean>
        </beans:property>
        <beans:property name="key" value="an_id_for_this_auth_provider_only"/>
    </beans:bean>
    <!-- 认证类 -->
    <beans:bean id="userDetailsService" class="com.itxk.core.service.UserDetailServiceImpl"/>


    <!-- 单点登出  开始  -->
    <beans:bean id="singleLogoutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter"/>
    <!-- 经过此配置,当用户在地址栏输入本地工程 /logout/cas  -->
    <beans:bean id="requestSingleLogoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
        <beans:constructor-arg value="http://192.168.1.88:9100/cas/logout?service=http://127.0.0.1:8086"/>
        <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>

?

创建认证类

?

/**
 * 自定义认证类:
 * 在之前这里负责用户名密码的校验工作, 并给给当前用户赋予对应的访问权限
 * 现在cas和springSecurity集成, 集成后, 用户名密码的校验工作交给cas完成, 所以能够进入到
 * 这里类的方法中的都是已经成功认证的用户, 这里只需要给登录过的用户赋予对应的访问权限就可以
 */
public class UserDetailServiceImpl implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //创建权限集合
        List<GrantedAuthority> authorityList = new ArrayList<>();
        //向权限集合中加入访问权限
        authorityList.add(new SimpleGrantedAuthority("ROLE_USER"));
        return new User(username, "", authorityList);
    }
}

前端工程添加CAS

在web.xml当中添加过滤器

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

添加配置文件spring/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">

    <!-- 放行静态资源-->
    <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="/index.html" security="none"></http>
    <http pattern="/search.html" security="none"></http>
    <http pattern="/cart.html" security="none"></http>

    <!--   entry-point-ref  入口点引用 -->
    <http use-expressions="false" entry-point-ref="casProcessingFilterEntryPoint">
        <!-- 匿名角色 IS_AUTHENTICATED_ANONYMOUSLY -->
        <intercept-url pattern="/cart/*.do" access="IS_AUTHENTICATED_ANONYMOUSLY"></intercept-url>
        <intercept-url pattern="/itemsearch/*.do" access="IS_AUTHENTICATED_ANONYMOUSLY"></intercept-url>
        <!--配置拦截器, 拦截所有请求, 应该具有ROLE_USER的权限才可以访问我们系统-->
        <intercept-url pattern="/**" access="ROLE_USER"/>
        <csrf disabled="true"/>
        <!-- custom-filter为过滤器,
        position 表示将过滤器放在指定的位置上,
        before表示放在指定位置之前  ,
        after表示放在指定的位置之后  -->
        <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://192.168.1.88:9100/cas/login"/>
        <beans:property name="serviceProperties" ref="serviceProperties"/>
    </beans:bean>
    <beans:bean id="serviceProperties" class="org.springframework.security.cas.ServiceProperties">
        <!--service 配置自身工程的根地址+/login/cas   -->
        <beans:property name="service" value="http://127.0.0.1:8083/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"/>
        <!-- ticketValidator 为票据验证器 -->
        <beans:property name="ticketValidator">
            <beans:bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
                <beans:constructor-arg index="0" value="http://192.168.1.88:9100/cas"/>
            </beans:bean>
        </beans:property>
        <beans:property name="key" value="an_id_for_this_auth_provider_only"/>
    </beans:bean>
    <!-- 认证类 -->
    <beans:bean id="userDetailsService" class="com.itxk.core.service.UserDetailServiceImpl"/>


    <!-- 单点登出  开始  -->
    <beans:bean id="singleLogoutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter"/>
    <!-- 经过此配置,当用户在地址栏输入本地工程 /logout/cas  -->
    <beans:bean id="requestSingleLogoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
        <beans:constructor-arg value="http://192.168.1.88:9100/cas/logout?service=http://127.0.0.1:8083"/>
        <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>

创建认证类

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;
/**
 * 自定义认证类:
 * 在之前这里负责用户名密码的校验工作, 并给给当前用户赋予对应的访问权限
 * 现在cas和springSecurity集成, 集成后, 用户名密码的校验工作交给cas完成, 所以能够进入到
 * 这里类的方法中的都是已经成功认证的用户, 这里只需要给登录过的用户赋予对应的访问权限就可以
 */
public class UserDetailServiceImpl implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //创建权限集合
        List<GrantedAuthority> authorityList = new ArrayList<>();
        //向权限集合中加入访问权限
        authorityList.add(new SimpleGrantedAuthority("ROLE_USER"));
        return new User(username, "", authorityList);
    }
}

?

以上是关于使用CAS实现单点登录功能的主要内容,如果未能解决你的问题,请参考以下文章

discuz x2怎么实现cas单点登录?

CAS单点登录示例

CAS单点登录,怎么实现注册后自动登录。用java实现

单点登录CAS使用记:使用maven的overlay实现无侵入的改造CAS

附代码单点登录介绍和服务端实现

cas有些请求路径不需要单点登录过滤器拦截