CAS与Spring的集成

简介

CAS项目官网 是一款开源的单点登陆解决方案,能够直接再Tomcat,Jetty等WEB容器上运行,支持多种开发语言css

下载

下载cas-server-XXX-release.zip(地址html

注意:官网提供的全部版本中,最新的版本(4.0以上)可能没有release.zip包java

修改tomcat

建立秘钥

生成keypair mysql

$JRE_HOME/bin/ 目录下建立命令行,输入如下命令,其中CN的值必须保持和主机名一致,假设密码为 mypass,再继续填写信息,遇到主密码直接回车web

keytool -genkeypair -alias cas -keyalg RSA -storepass mypass

上述命令会在用户目录下生成一个.keystore文件,后面会使用该文件其余的几个经常使用命令spring

# 查看 keypair:
keytool -list -storepass mypass
# 删除 keypair:
keytool -delete -alias <别名> -storepass mypasssql

导出证书 数据库

keytool -exportcert -alias cas -file cas.crt -storepass mypass

上述命令会在当前目录生成一个cas.crt证书文件,后面也会使用该文件apache

向JVM导入证书 windows

keytool -importcert -alias cas -file cas.crt -keystore "%JAVA_HOME%\jre\lib\security\cacerts" -storepass mypass -noprompt

上面的JAVA_HOME须要换成实际的路径

配置tomcat

修改配置文件

不一样版本的cas对jdk版本的要求不同,请查阅cas说明

修改tomcat目录下的conf/server.xml,打开SSL

<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="off" />
配置HTTPS端口
<Connector    port="8443" 
              protocol="org.apache.coyote.http11.Http11Protocol" 
              SSLEnabled="true"
              maxThreads="150" 
              scheme="https" 
              secure="true"
              clientAuth="false" 
              sslProtocol="TLS" 
              keystoreFile="apache-tomcat-7.0.73\conf\security\.keystore"
              keystorePass="mypass"/>

若是要确保访问HTTPS服务只能用8443端口,则须要禁用其余端口号

访问cas服务

重启tomcat: 在 tomcat/bin/ 下先执行 shutdown,再执行 startup。访问cas的地址为 https://localhost:8443/cas/login

WEB集成CAS-CLIENT

在Spring服务的pom.xml加入cas-client的依赖包

<!--cas client-->
 <dependency>
     <groupId>org.jasig.cas.client</groupId>
     <artifactId>cas-client-core</artifactId>
     <version>3.3.3</version>
 </dependency>

在Spring的项目配置文件web.xml中加入cas的配置

<!-- ======================== 单点登陆开始 ======================== -->
 <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>CAS Filter</filter-name>
     <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>
     <init-param>
         <param-name>casServerLoginUrl</param-name>
         <param-value>https://nkgy4l003835331:8443/cas/login</param-value>
         <!-- 这里只能使用主机名的url -->
     </init-param>
     <init-param>
         <param-name>serverName</param-name>
         <param-value>http://localhost:8080</param-value>
     </init-param>
 </filter>
 <filter-mapping>
     <filter-name>CAS Filter</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>https://nkgy4l003835331:8443/cas</param-value>
         <!-- 这里只能使用主机名的url -->
     </init-param>
     <init-param>
         <param-name>serverName</param-name>
         <param-value>http://localhost:8080</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>

 <!-- ======================== 单点登陆结束 ======================== -->

同一个Tomcat下部署CAS-Server和Spring Web须要在tomcat的server.xml中建立两个Service

<!-- 用于HTTP应用 -->
  <Service name="Catalina"> 
  </Service>
  
  <!-- 用于CAS的HTTPS应用 -->
  <Service name="Catalina.cas">
  </Service>

将HTTPS的移动到Catalina.cas中,并配置context的path路径cas的包路径

<Service name="Catalina.cas">
  <Connector port="8443" 
            protocol="org.apache.coyote.http11.Http11Protocol" SSLEnabled="true"
            maxThreads="150" scheme="https" secure="true"
            clientAuth="false" sslProtocol="TLS"
            keystoreFile="F:\software\apache-tomcat-7.0.73-windows-x64\apache-tomcat-7.0.73\conf\security\keystore.jks"
            keystorePass="changeit"/>
  <Engine name="Catalina.cas" defaultHost="localhost">
  <Host name="localhost"  appBase="webapps" unpackWARs="true" autoDeploy="false" deployOnStartup="false">
   <Context path="/cas" docBase="cas" reloadable="false" />

配置HTTP的context

与上一步相似,context的path填WEB项目的地址

CAS链接数据库

复制cas下载包中的 cas-server-support-jdbc-*.jartomcat/webapps/cas/WEB-INF/lib
复制数据库驱动包(如 mysql-connector.jar )到 tomcat/webapps/cas/WEB-INF/lib

  • 修改cas配置文件: 在 tomcat/webapps/cas/WEB-INF/cas.properties 中加入如下数据库的配置
# == Basic database connection pool configuration ==
  database.driverClass=com.mysql.jdbc.Driver
  database.url=jdbc:mysql://127.0.0.1:3306/test
  database.user=root 
  database.password=root
  • 修改 tomcat/webapps/cas/WEB-INF/deployerConfigContext.xml 配置

    • 注册dataSource的bean
    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource"
        p:driverClassName="${database.driverClass}"
        p:url="${database.url}"
        p:username="${database.user}"
        p:password="${database.password}">
      </bean>
    • 注册密码加密的bean,这里的bean的实现类能够是自定义的加密类,只要实现encode方法就能够
    // 如下使用cas自带的加密类
      <bean id="md5PasswordEncoder" class="org.jasig.cas.authentication.handler.DefaultPasswordEncoder">
          <constructor-arg index="0">
              <value>MD5</value>
          </constructor-arg>
      </bean>
    • 注释默认的校验规则bean,默认判断用户名和密码相等便可
    <!-- <bean class="org.jasig.cas.authentication.handler.support.SimpleTestUsernamePasswordAuthenticationHandler" /> -->
    • 在上述注释后面添加新的校验bean,注意sql的值要彻底和设计的登录表对应
    <bean class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler">
          <property name="dataSource" ref="dataSource"></property>
          <property name="sql" value="select password from tb_user where account=?"></property>
          <property name="passwordEncoder" ref="md5PasswordEncoder"></property>
      </bean>

重启tomcat从新登录

CAS自定义登录页面

CAS的登录页jsp文件在 tomcat/webapps/cas/WEB-INF/view/jsp/default/ui/casLoginView.jsp

修改注意事项

  • 能够自由定义样式和脚本
  • 要引入静态资源,须要将被引入的文件放在 tomcat/webapps/cas 目录下的位置,能够参考 tomcat/webapps/cas/css ,引用代码示例:
<link href="/css/login.css" rel="stylesheet" type="text/css">

保持如下内容完整,由于如下内容会将值传给login请求

<form:form method="post" id="fm1" cssClass="fm-v clearfix" commandName="${commandName}" htmlEscape="true">
<input type="hidden" name="lt" value="${loginTicket}" />
<input type="hidden" name="execution" value="${flowExecutionKey}" />
<input type="hidden" name="_eventId" value="submit" />
<form:input id="username" .../>
<form:password id="password" .../>

原有的控件具备友好的功能,能够适当考虑保留和优化,以下面是登录失败提示凭证错误的控件
<form:errors path="*" id="msg" cssClass="errors" element="div" />
保存文件,刷新浏览器便可

CAS加入注册功能(扩展:其余任意功能)

在CAS Server上添加注册功能

CAS自己不具备注册功能,由于CAS要开放注册业务给其余业务系统,CAS单一负责登录校验和会话管理

修改步骤

tomcat/webapps/cas/WEB-INF/view/jsp/default/ui/中加入注册页的jsp文件,如casRegisterView.jsp,传入参数为usernamepassword
tomcat/webapps/cas/WEB_INF/classes/default_views.properties 中添加注册页的view映射

  • 添加注册页

    • casRegisterView.(class)=org.springframework.web.servlet.view.JstlView
    • casRegisterView.url=/WEB-INF/view/jsp/default/ui/casRegisterView.jsp
  • 建立注册控制类RegisterController.java,输入如下代码
package x.y.z;
 import org.springframework.web.servlet.ModelAndView;
 import org.springframework.web.servlet.mvc.AbstractController;

 //必须继承AbstractController控制器基础类
 public class RegisterController extends AbstractController
 {
      private DatabaseHandler dataHandler;//数据库处理类(bean注入)
      
      // 必须实现的方法
      protected ModelAndView handleRequestInternal(HttpServletRequest request,
                  HttpServletResponse response) throws Exception
      {
          ModelAndView signinView=new ModelAndView();
          String username=request.getParameter("username");
          String password=request.getParameter("password");
          if(username == null || username.equals("") || password == null || password.equals("")){
              return new ModelAndView("casRegisterView");
          }
          boolean success = this.dataHandler.insertUser(username,password);
          String viewName=getSignInView(request);
          signinView.setViewName(viewName);
          return signinView;
      }
      // 处理跳转url
      protected String getSignInView(HttpServletRequest request) {
          String service = ServletRequestUtils.getStringParameter(request, "service", "");
          return ("redirect:login" + (service.length() > 0 ? "?service=" + service : ""));
      }
      // 开放设置dataHandler,用于bean注入
      public void setDataHandler(DatabaseHandler handler){
         this.dataHandler = handler;
      }
 }
  • 建立数据库实现类DatabaseHandler.java,输入如下代码
package x.y.z;
 import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
 import javax.sql.DataSource;
 
 class DatabaseHandler{
     // 数据库操做模板类
     private SimpleJdbcTemplate jdbcTemplate;
     // 数据源
     private DataSource dataSource;
     // 自定义方法,示例为插入一个新用户
     public boolean insertUser(String username,String password){
       int r = 0;
       try {
         r = this.jdbcTemplate.update("insert into tb_user(account,password,time) values(?,?,?)",username,password,System.currentTimeMillis()/1000);
       }catch (Exception e){
         e.printStackTrace();
         return false;
       }
       return r > 0;
     }
     //设置datasource注入
     public final void setDataSource(final DataSource dataSource) {
         this.jdbcTemplate = new SimpleJdbcTemplate(dataSource);
         this.dataSource = dataSource;
     }
 }
  • 将3-4的两个java文件打包成jar文件,并最终放到 tomcat/webapps/cas/WEB-INF/lib

    • 第一步: 编译java文件
    # 因为java文件中引用了其余类,因此须要用 -cp 连接jar包
      javac -cp "tomcat/webapps/cas/WEB-INF/lib/*" RegisterController.java.java
      javac -cp "tomcat/webapps/cas/WEB-INF/lib/*" DatabaseHandler.java.java
    • 第二步: 打包成jar包
    # 能够将两个文件打包在一块儿,也能够分开打包。例如打包在一块儿,名字取为my-register.jar
      # @注意: 因为3-4建立的java文件包含package 值,因此用jar打包时必须将class文件放在当前目录下的package子目录下,例如当前目录下的 x/y/z/目录下
      jar cvf my-register.jar x/y/z/*.class
  • 修改 tomcat/webapps/cas/WEB-INF/cas-servlet.xml 文件配置

    • 注册dataHandler的bean
    <bean id="dataHandler" class="com.cas.luodong.web.DatabaseHandler">
          <property name="dataSource" ref="dataSource"></property>
      </bean>
    • 注册registerController的bean
    <bean id="registerController" class="com.cas.luodong.web.RegisterController"
              p:dataHandler-ref="dataHandler"/>
    • 在 <bean id="handlerMappingC" 的props中加入如下内容
    <prop key="/register">registerController</prop>
    修改 tomcat/webapps/cas/WEB-INF/web.xml 文件配置
    • 加入servlet-mapping项
    <servlet-mapping>
          <servlet-name>cas</servlet-name>
          <url-pattern>/validate</url-pattern>
      </servlet-mapping>

重启tomcat,访问 cas/register 能够看到结果

FQA

  • java.lang.IllegalArgumentException: casServerUrlPrefix cannot be null

解决办法: cas-client-core的版本3.4.1不支持RC5,改成3.3.3便可

  • java.security.cert.CertificateException: No name matching localhost found

解决办法: jdk生成证书时填写的CN值必须为当前主机的主机名

  • java.security.cert.CertificateException: No subject alternative names present

解决办法: web.xml中的 casServerLoginUrl 和 casServerUrlPrefix 不能使用ip,必须使用CN值

  • CAS is Unavailable ; There was an error trying to complete your request. Please notify your support desk or try again.

解决办法: 检查ui下的jsp文件是否有误,确认标签匹配正确

相关文章
相关标签/搜索