CAS笔记: 部署与测试

CAS 简介

cas是YALE大学发起的一个开源项目, 旨在为web应用系统提供一种可靠的单点登录方法.

它分为server和client端, server端负责对用户的认证工作, client端则负责处理对客户端受保护的资源的访问请求.

CAS的原理,如图:

CAS笔记: 部署与测试_第1张图片
cas-01.jpg

CAS 名词

  • Service Ticket: 简称ST, ST是CAS为用户签发的访问某一service的票据。用户访问service时,service发现用户没有ST,则要求用户去CAS获取ST。用户向CAS发出获取ST的请求,如果用户的请求中包含cookie,则CAS会以此cookie值为key查询缓存中有无TGT,如果存在TGT,则用此TGT签发一个ST,返回给用户。用户凭借ST去访问service,service拿ST去CAS验证,验证通过后,允许用户访问资源。

  • Ticket granting ticket: 简称TGT. 是cas服务器为用户签发的登录票据.拥有了TGT,用户就可以证明自己在CAS成功登录过。TGT封装了Cookie值以及此Cookie值对应的用户信息。用户在CAS认证成功后,CAS生成cookie,写入浏览器,同时生成一个TGT对象,放入自己的缓存,TGT对象的ID就是cookie的值。当HTTP再次请求到来时,如果传过来的有CAS生成的cookie,则CAS以此cookie值为key查询缓存中有无TGT ,如果有的话,则说明用户之前登录过,如果没有,则用户需要重新登录。

  • Ticket granting cookie: 简称TGC. 这是一个cookie, 是cas服务器放到用户浏览器中用以标识用户身份的cookie.

CAS REST服务部署

stackoverflow参考资料

部署前的准备

  • 服务端创建证书
keytool -genkey -alias SomeName -keyalg RSA -keystore d:/your/dir/target.keystore

接着根据提示输入相关信息.在最后,提示输入密码时, 务必记住你输入的密码.

  • 服务端导出证书
keytool -export -file d:/your/dir/target.crt -alias SomeName -keystore d:/your/dir/target.keystore

导出时, 会提示你输入刚才创建keystore时的密码.
导出完成后, 生成的target.crt就可以分发给客户端的jdk使用了.

  • 客户端导入证书
keytool -import -keystore %JAVA_HOME%/jre/lib/security/cacerts -file d:/your/dir/target.crt -alias SomeName

提示输入密码. 如果出现keytool error: java.io.IOException: Keystore was tampered with, or password was incorrect错误, 则使用密码changeit.

  • 在服务端tomcat服务器上应用证书



  • 启动tomcat服务器, 验证SSL是否启用

    访问地址https://localhost:8443/

生成支持rest的cas.war

新建目录, 编写pom.xml, 使用命令mvn clean package生成cas.war




  
    org.jasig.cas
    cas-server
    3.4.12
  

  4.0.0
  h.usm.my
  cas
  war
  1.0
  HUSM CAS Web Application

  
    3.4.12
    UTF-8
  

  
    
      org.jasig.cas
      cas-server-webapp
      ${cas.version}
      war
      runtime
    

    
     org.jasig.cas
     cas-server-support-jdbc
     ${cas.version}
   

    
      org.jasig.cas
      cas-server-integration-restlet
      ${cas.version}
      jar
      
        
          org.springframework
          spring-web
        
      
    

    
        com.h2database
        h2
        1.4.187
    

    
      org.hibernate
      hibernate-core
      ${hibernate.core.version}
      jar
    
    
      org.hibernate
      hibernate-entitymanager
      3.6.0.Final
    
  

  
    
      ja-sig
      http://oss.sonatype.org/content/repositories/releases
    
  

  
        
            
                 maven-war-plugin
                             
                                 cas
                             
                        
        
    

修改cas.war的web.xml, 填写Rest Servlet


    
        restlet
        com.noelios.restlet.ext.spring.RestletFrameworkServlet
        1
    

    
        restlet
        /rest/*
    

修改cas.war的deployerConfigContext.xml, 修改用户名密码的验证方式

  • 注释掉默认的SimpleTestUsernamePasswordAuthenticationHandler

  • 添加新的AuthentitcationHandler


    
    
    

这里使用了数据库来存储用户的帐号与密码.
验证时使用sql进行查询,并对查询获得的password字段值,与使用MD5PasswordEncoder进行加密后的输入密码, 进行比对验证.

相关的dataSource与encoder配置如下:


   
   
   
   

 

    
        MD5
    

通过org.jasig.cas.authentication.handler.PasswordEncoder接口可实现自定义加密类.
记得添加相应的数据库驱动jar包到lib目录下.

验证

访问 https://localhost:8443/cas, 输入账密进行网页验证.

CAS Rest的java验证代码


import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;

import javax.net.ssl.HttpsURLConnection;

public class TestCasRest {
    
    /**
     * resolve exception:
     *      java.security.cert.CertificateException: No name matching localhost found
     */
    static {
        //for localhost testing only
        javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(
        new javax.net.ssl.HostnameVerifier(){
 
            public boolean verify(String hostname,
                    javax.net.ssl.SSLSession sslSession) {
                if (hostname.equals("localhost")) {
                    return true;
                }
                return false;
            }
        });
    }

    public static void main(String... args) throws Exception {
        String username = "alpha";
        String password = "123";
        validateFromCAS(username, password);
    }

    public static boolean validateFromCAS(String username, String password)
            throws Exception {

        String url = "https://localhost:8443/cas/rest/tickets";
        try {
            HttpsURLConnection hsu = (HttpsURLConnection) openConn(url);
            String s = URLEncoder.encode("username", "UTF-8") + "="
                    + URLEncoder.encode(username, "UTF-8");
            s += "&" + URLEncoder.encode("password", "UTF-8") + "="
                    + URLEncoder.encode(password, "UTF-8");

            System.out.println(s);
            OutputStreamWriter out = new OutputStreamWriter(
                    hsu.getOutputStream());
            BufferedWriter bwr = new BufferedWriter(out);
            bwr.write(s);
            bwr.flush();
            bwr.close();
            out.close();

            String tgt = hsu.getHeaderField("location");
            System.out.println("ResponseCode: " + hsu.getResponseCode());
            if (tgt != null && hsu.getResponseCode() == 201) {
                System.out.println(tgt);

                System.out.println("==> TGT is : "
                        + tgt.substring(tgt.lastIndexOf("/") + 1));
                tgt = tgt.substring(tgt.lastIndexOf("/") + 1);
                bwr.close();
                closeConn(hsu);

                String serviceURL = "http://localhost:8080/CasClient";
                String encodedServiceURL = URLEncoder
                        .encode("service", "utf-8")
                        + "="
                        + URLEncoder.encode(serviceURL, "utf-8");
                System.out.println("Service url is : " + encodedServiceURL);

                String myURL = url + "/" + tgt;
                System.out.println(myURL);
                hsu = (HttpsURLConnection) openConn(myURL);
                out = new OutputStreamWriter(hsu.getOutputStream());
                bwr = new BufferedWriter(out);
                bwr.write(encodedServiceURL);
                bwr.flush();
                bwr.close();
                out.close();

                System.out.println("Response code is:  "
                        + hsu.getResponseCode());

                BufferedReader isr = new BufferedReader(new InputStreamReader(
                        hsu.getInputStream()));
                String line;
                System.out.println(hsu.getResponseCode());
                while ((line = isr.readLine()) != null) {
                    System.out.println("==> ST is : " + line);
                }
                isr.close();
                hsu.disconnect();
                return true;

            } else {
                return false;
            }

        } catch (MalformedURLException mue) {
            mue.printStackTrace();
            throw mue;

        } catch (IOException ioe) {
            ioe.printStackTrace();
            throw ioe;
        }

    }

    static URLConnection openConn(String urlk) throws MalformedURLException,
            IOException {

        URL url = new URL(urlk);
        HttpsURLConnection hsu = (HttpsURLConnection) url.openConnection();
        hsu.setDoInput(true);
        hsu.setDoOutput(true);
        hsu.setRequestMethod("POST");
        return hsu;

    }

    static void closeConn(HttpsURLConnection c) {
        c.disconnect();
    }

}

Cas client端(非REST请求方式)的配置

在client端工程添加cas-client-core.jar包及相关依赖


    org.jasig.cas.client
    cas-client-core
    3.1.12

修改client端工程的web.xml, 添加cas的过滤器



org.jasig.cas.client.session.SingleSignOutHttpSessionListener

 


    CAS Single Sign Out Filter
    org.jasig.cas.client.session.SingleSignOutFilter


    CAS Single Sign Out Filter
    /*

 


    CASFilter
    org.jasig.cas.client.authentication.AuthenticationFilter
    
        casServerLoginUrl
        https://sso.wsria.com:8443/cas/login
    
    
        
        serverName
        http://localhost:10000
    


    CASFilter
    /*

 


    CAS Validation Filter
    
org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter
    
        casServerUrlPrefix
        https://sso.wsria.com:8443/cas
    
    
        serverName
        http://localhost:10000
    


    CAS Validation Filter
    /*

 


    CAS HttpServletRequest Wrapper Filter
    
org.jasig.cas.client.util.HttpServletRequestWrapperFilter


    CAS HttpServletRequest Wrapper Filter
    /*

 


    CAS Assertion Thread Local Filter
    org.jasig.cas.client.util.AssertionThreadLocalFilter


    CAS Assertion Thread Local Filter
    /*

 


    AutoSetUserAdapterFilter
    AutoSetUserAdapterFilter
    com.wsria.demo.filter.AutoSetUserAdapterFilter


    AutoSetUserAdapterFilter
    /*


其中自定义的AutoSetUserAdapterFilter的代码如下

package com.wsria.demo.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.jasig.cas.client.validation.Assertion;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import com.wsria.demo.entity.account.User;
import com.wsria.demo.service.account.UserManager;
import com.wsria.demo.util.UserUtil;


/**
 * 自动根据单点登录系统的信息设置本系统的用户信息
 *
 * @author 咖啡兔
 * @site www.wsria.cn
 *
 */
public class AutoSetUserAdapterFilter implements Filter {
        
        /**
         * Default constructor. 
         */
        public AutoSetUserAdapterFilter() {
        }

        /**
         * @see Filter#destroy()
         */
        public void destroy() {
        }

        /**
         * 过滤逻辑:首先判断单点登录的账户是否已经存在本系统中,
         * 如果不存在使用用户查询接口查询出用户对象并设置在Session中
         * @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
         */
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
                        ServletException {
                HttpServletRequest httpRequest = (HttpServletRequest) request;
                
                // _const_cas_assertion_是CAS中存放登录用户名的session标志
                Object object = httpRequest.getSession().getAttribute("_const_cas_assertion_");
                
                if (object != null) {
                        Assertion assertion = (Assertion) object;
                        String loginName = assertion.getPrincipal().getName();
                        User user = UserUtil.getCurrentUser(httpRequest.getSession());
                        
                        // 第一次登录系统
                        if (user == null) {
                                WebApplicationContext wct = WebApplicationContextUtils.getWebApplicationContext(httpRequest
                                                .getSession().getServletContext());
                                UserManager userManager = (UserManager) wct.getBean("userManager");
                                user = userManager.findUserByLoginName(loginName);
                                // 保存用户信息到Session
                                UserUtil.saveUserToSession(httpRequest.getSession(), user);
                        }
                        
                }
                chain.doFilter(request, response);
        }

        /**
         * @see Filter#init(FilterConfig)
         */
        public void init(FilterConfig fConfig) throws ServletException {
        }

}

附注

单点退出

访问https://localhost:8443/cas/logout即可.

美化CAS服务器界面

修改cas\WEB-INF\view\jsp\default\ui下相关的jsp文件

在服务端不使用SSL协议

  • 修改%CATALINA_HOME%\conf\server.xml文件, 关闭Tomcat服务器的SSL端口

  • 修改服务端cas\WEB-INF\deployerConfigContext.xml文件

  
  • 修改服务端的cas\WEB-INF\spring-configuration\ticketGrantingTicketCookieGennerator.xml文件

  

你可能感兴趣的:(CAS笔记: 部署与测试)