搭建CAS协议的SSO单点登录系统

概念:

单点登录( Single Sign-On , 简称 SSO)是目前较为流行的服务于企业业务整合的解决方案之一,在多个应用系统中,用户只需要登录一次,就可以访问所有相互信任的应用系统。

主要包括两部分:CAS Server和CAS Client

如何搭建CAS Server服务端?,如下所示:

1.windows系统下,修改hosts 文件的方法添加域名,建立自定义域名和IP之前映射关系。         

具体文件位置:C:\Windows\System32\drivers\etc\hosts

2.安装JDK8安装配置,这里不再描述.

3.使用keytool工具,配置安全证书     (需要处于jdk的bin目录下,例如:   D:\java\jdk1.8\bin)

生成证书:keytool -genkey -alias ssodemo -keyalg RSA -keysize 1024 -keypass michaelpwd -validity 365 -keystore D:\SSO\keytool\ssodemo.keystore -storepass michaelpwd

  • 截图中需要输入的姓名和上面hosts文件中配置的一致;
  • keypass 和 storepass 两个密码要一致,否则下面tomcat 配置https 访问失败;

导出证书:keytool -export -alias ssodemo -keystore D:\SSO\keytool\ssodemo.keystore -file D:\SSO\keytool\ssodemo.crt -storepass michaelpwd

客户端导入证书:keytool -import -keystore D:\java\jdk1.8\jre\lib\security\cacerts -file D:\SSO\keytool\ssodemo.crt -alias ssodemo

默认密码是:changeit

4.配置CAS Server的tomcat,我这里使用的是tomcat8,apache-tomcat-8.5.57-windows-x64.zip

在文件 conf/server.xml文件中修改内容如下:

验证https配置,双击startup.bat,启动tomcat,访问https://demo.micmiu.com:8443即可

5.部署CAS-Server

下载cas-overlay-template-5.2.zip源码,编译后生成cas.war包,并复制到第4步的Tomcat\webapps下,启动tomcat。在浏览器地址栏输入:https://demo.micmiu.com:8443/cas/login,出现如下所示:

看到上述页面表示CAS-Server已经部署成功。

其中修改登录的用户名/密码文件(已经修改成admin/admin)

路径如下:tomcat-ca\webapps\cas\WEB-INF\classes\application.properties

默认是不支持HTTP,如果需要支持HTTP,则需要修改cas.war的配置文件,增加http

路径:webapps\cas\WEB-INF\classes\services\HTTPSandIMAPS-10000001.json

6.部署CAS-client

下载相关jar包,cas-client-core-3.6.1.jar,在web.xml配置文件中增加filter配置

有关cas-client的web.xml修改的详细说明见官网介绍:

Confluence

如果在同一台物理机上,记得修改CAS- client的tomcat端口,避免与CAS-server的tomcat端口重复。

7.基本的测试

预期流程: 打开CAS- client url —-> 跳转CAS-server 验证 —-> 显示CAS- client的应用 

提供CAS的官网:CAS | Apereo

8.CAS client参数的多种策略配置方式,一共有5中配置方式

如果不配置,默认DEFAULT

第一种:默认方式(DEFAULT),实现类:LegacyConfigurationStrategyImpl

先webXmlConfigurationStrategy策略,如果获取不到参数,再jndiConfigurationStrategy策略

也就是说先第二种方式获取,如果获取不到用第三种方式获取。

第二种:web.xml配置方式(WEB_XML),实现类:WebXmlConfigurationStrategyImpl

最常见的配置方式,在web.xml中配置casServerLoginUrl,casServerUrlPrefix,serverName

这里就不多说了。

第三种:JNDI配置方式(JNDI),实现类:JndiConfigurationStrategyImpl

jndi(Java Naming and Directory Interface,Java命名和目录接口)是一组在Java应用中访问命名和目录服务的API

不常用,有兴趣的自行百度。

需要在web.xml中增加如下配置:

    <context-param>
        <param-name>configurationStrategy</param-name>
        <param-value>JNDI</param-value>
    </context-param>

第四种:配置文件方式(PROPERTY_FILE),比较推荐,实现类:PropertiesConfigurationStrategyImpl

需要在web.xml中增加如下配置:

    <context-param>
        <param-name>configurationStrategy</param-name>
        <param-value>PROPERTY_FILE</param-value>
    </context-param>

    <context-param>
        <param-name>configFileLocation</param-name>
        <param-value>/opt/cas.properties</param-value>
    </context-param>

这里configFileLocation的值需要绝对路径(classpath:cas.properties不可以),因为源码中无法解析classpath字符串,

源码的实现方式如图所示:

,在cas.properties文件中配置casServerLoginUrl,casServerUrlPrefix,serverName等参数

serverName=http://xxx:8080
casServerLoginUrl=https://xxx:8443/cas/login
casServerUrlPrefix=https://xxx:8443/cas

第五种:设置系统参数方式(SYSTEM_PROPERTIES),比较推荐,

需要在web.xml中增加如下配置:

    <context-param>
        <param-name>configurationStrategy</param-name>
        <param-value>SYSTEM_PROPERTIES</param-value>
    </context-param>

 可增加上下文监听器,将CAS client所需要的的参数设置到系统环境中去

代码如下:

先读取cas.properties文件内容,然后利用servlet上下文监听器,配置casServerLoginUrl,casServerUrlPrefix,serverName等参数信息到系统环境中。

或者

配置项目启动的系统参数

在选择项目按右键->Run as->Run as configurations…;在VM arguments后追加-DXXX=****(-D不能省略),这样就可以通过 System.getProperty(“XXX”)获取****了

或者

通过tomca服务器加载时候定义变量

这里还分windows版本和linux版本,以tomcat7为例。

1)windows环境下:

编辑tomcat7主目录\bin 下的catalina.bat,在第二行定义XXXX,然后就可以System.getProperty(“XXX”)获取****了。

2)linux环境下:

不要像windows设置一样,不要加set 血的教训。

到此开源cas-client-core-3.6.1.jar包 CAS client集成结束。

/*********************************************************自定义实现过滤器***********************************************************/

1.AuthenticationFilter

@Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest)request;
        HttpServletResponse resp = (HttpServletResponse)response;
        HttpSession httpSession = req.getSession();
        
        String reqUrl = req.getRequestURL().toString();
        //http://127.0.0.1:8090/GBaseConsole/test
//        String[] urlArry = reqUrl.split("://");
//        System.out.println("==========urlArry==========");
//        System.out.println(urlArry[0]);
//        System.out.println(urlArry[1]);
//        System.out.println("===========================");
//        String ssoClient = urlArry[0] + "://" + urlArry[1].substring(0, urlArry[1].indexOf("/"))
//                + req.getServletContext().getContextPath();
        // 给Constants中SSO_CLIENT赋值
        //Constants.SSO_CLIENT = reqUrl;
        System.out.println("ssoClient="+reqUrl);
        
        
        String ticket = (String) req.getParameter("ticket");
        System.out.println("AuthenticationFilter,ticket="+ticket);
        String backurl = request.getParameter("backUrl");
        System.out.println("AuthenticationFilter,backurl: " + backurl);
        if(backurl==null||"".equals(backurl)){
            String url = req.getQueryString();
            if(url != null && !"".equals(url) && url.lastIndexOf("backUrl")!=-1){
                backurl = url.substring(url.lastIndexOf("backUrl")+8);
            }
        }
        System.out.println("backurl="+backurl);
        
        UserSession userSession = (UserSession)httpSession.getAttribute(Constants.SESSION_KEY);
        
        if((ticket == null || ticket.equals("")) && userSession == null){  //没有登录,重定向到登录页面
            String encodeUrl = URLEncoder.encode(reqUrl + "?backUrl="+backurl, "utf-8");
            String redirectUrl = "https://" + Constants.SSO_IAM_SERVER + ":" + Constants.SSO_IAM_PORT + "/cas/login?service=" + encodeUrl;
            System.out.println("AuthenticationFilter=========redirectUrl: " + redirectUrl);
            resp.sendRedirect(redirectUrl);
        }else{ 
            chain.doFilter(request, response);
        }
    }

 2.Cas20ProxyReceivingTicketValidationFilter

@Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        ByteArrayOutputStream bos = null;
        BufferedReader br = null;
        try {
            HttpServletRequest req = (HttpServletRequest) request;
            HttpServletResponse resp = (HttpServletResponse) response;
            HttpSession session = req.getSession();

            UserSession userSession = (UserSession) session.getAttribute(Constants.SESSION_KEY);

            // userId不为空表示是进入到LoginedServlet的跳转
            String userId = (String) req.getParameter("userId");
            String userName = (String) req.getParameter("userName");
            if (userSession != null || (userId != null && userName != null)) {
                chain.doFilter(request, response);
                return;
            }

            // 没有session,则SSO验证
            String reqUrl = req.getRequestURL().toString();
            
            String ticket = req.getParameter("ticket");
            String backUrl = req.getParameter("backUrl");
            System.out.println("cas20 filter backUrl=" + backUrl);
            if(backUrl!=null && !"".equals(backUrl)){
                backUrl = URLEncoder.encode(backUrl, "utf-8");
            }
            
            System.out.println("Cas20ProxyReceivingTicketValidationFilter====SSO_CLIENT="+ reqUrl);
            String encodeUrl = URLEncoder.encode(reqUrl + "?backUrl="+backUrl, "utf-8");
            String validateUrl = "https://" + Constants.SSO_IAM_SERVER + ":" + Constants.SSO_IAM_PORT
                    + "/cas/serviceValidate?ticket=" + ticket + "&service=" + encodeUrl;
            System.out.println("validateUrl= " + validateUrl);
            HttpsURLConnection httpsUrlConn = null; 
            
            //创建SSLContext对象,并使用我们指定的信任管理器初始化
            SSLContext sc = createSSLContext();
            URL url = new URL(validateUrl);
            
            httpsUrlConn = (HttpsURLConnection)url.openConnection();
            httpsUrlConn.setSSLSocketFactory(sc.getSocketFactory());
//            httpsUrlConn.setDoInput(true);
//            httpsUrlConn.setDoOutput(true);
//            httpsUrlConn.setUseCaches(false);
            httpsUrlConn.setRequestProperty("accept", "*/*");  
            httpsUrlConn.setRequestProperty("connection", "Keep-Alive");  
            httpsUrlConn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); 
            
            int resp_status = httpsUrlConn.getResponseCode();
            if(resp_status == HttpsURLConnection.HTTP_OK){
                
                br = new BufferedReader(new InputStreamReader(httpsUrlConn.getInputStream(),"utf-8"));
                bos = new ByteArrayOutputStream();
                
                String line;
                while((line=br.readLine())!=null){
                    bos.write(line.getBytes("utf-8"));
                }
                
                String respXML = new String(bos.toByteArray(),"utf-8");
                System.out.println("respXML="+respXML);
                
                String error = XmlUtils.getTextForElement(respXML, "authenticationFailure");
                System.out.println("=========authenticationFailure="+error);

                if (error != null && !error.equals("")) { // 认证失败
                    System.out.println("=========authenticationFailure="+error);
                    throw new TicketValidationException(error);
                } else { // 认证成功
                    // 获取用户名称
                    userId = XmlUtils.getTextForElement(respXML, "userId");
                    userName = XmlUtils.getTextForElement(respXML, "userName");
                    
                    System.out.println("Cas20ProxyReceivingTicketValidationFilter: userName="+userName);
                    System.out.println("Cas20ProxyReceivingTicketValidationFilter: userId="+userId);
                    System.out.println("Cas20ProxyReceivingTicketValidationFilter: backUrl="+backUrl);
//                    if(userName!=null && !"".equals(userName)){
//                        userName = URLEncoder.encode(userName, "utf-8");
//                    }
//                    if(userId!=null && !"".equals(userId)){
//                        userId = URLEncoder.encode(userId, "utf-8");
//                    }
//                    if(backUrl!=null && !"".equals(backUrl)){
//                        backUrl = URLEncoder.encode(backUrl, "utf-8");
//                    }
                    
                    UserSession user = new UserSession();
                    user.setUserId(userId);
                    user.setUserName(userName);
                    user.setTicket(ticket);
                    req.getSession().setAttribute(Constants.SESSION_KEY, user);
                    
                    System.out.println("cas filter user============ "+userId+", "+userName+", "+ticket);
                    
//                    chain.doFilter(req, resp);
//                    String redirectUrl = "/set/session?ticket=" + ticket + "&userId=" + userId + "&userName=" + userName + "&backUrl="+backUrl;
                    String redirectUrl = reqUrl+"?ticket=" + ticket + "&backUrl="+backUrl;
                    
                    System.out.println("validate success,redirectURL:"+redirectUrl);
                    resp.sendRedirect(redirectUrl);
                }
            }
            
        } catch (Exception e) {
            System.out.println("Cas20ProxyReceivingTicketValidationFilter  errors");
            e.printStackTrace();
        } finally{
            if(bos!=null){
                bos.close();
            }
            if(br!=null){
                br.close();
            }
        }
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("Cas20ProxyReceivingTicketValidationFilter过滤器初始化");
    }
    
    private static SSLContext createSSLContext() {
        try {
            TrustManager[] wrapped = new TrustManager[1];
            wrapped[0] = new X509ExtendedTrustManagerChild();
            SSLContext context = SSLContext.getInstance("SSL");
            context.init(null, wrapped, new SecureRandom());
            return context;
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalArgumentException("NoSuchAlgorithmException." + e.getMessage());
        } catch (KeyManagementException e) {
            throw new IllegalArgumentException("KeyManagementException." + e.getMessage());
        }
    }

    private static class X509ExtendedTrustManagerChild extends X509ExtendedTrustManager{
        @Override
        public void checkClientTrusted(X509Certificate[] arg0,
                                       String arg1)
                throws java.security.cert.CertificateException
        {
            // TODO Auto-generated method stub
        }

        @Override
        public void checkServerTrusted(X509Certificate[] arg0,
                                       String arg1)
                throws java.security.cert.CertificateException
        {
            // TODO Auto-generated method stub
        }

        @Override
        public X509Certificate[] getAcceptedIssuers()
        {
            // TODO Auto-generated method stub
            return new X509Certificate[0];
        }

        @Override
        public void checkClientTrusted(X509Certificate[] arg0,
                                       String arg1, Socket arg2)
                throws java.security.cert.CertificateException
        {
            // TODO Auto-generated method stub
        }

        @Override
        public void checkClientTrusted(X509Certificate[] arg0,
                                       String arg1, SSLEngine arg2)
                throws java.security.cert.CertificateException
        {
            // TODO Auto-generated method stub
        }

        @Override
        public void checkServerTrusted(X509Certificate[] arg0,
                                       String arg1, Socket arg2)
                throws java.security.cert.CertificateException
        {
            // TODO Auto-generated method stub
        }

        @Override
        public void checkServerTrusted(X509Certificate[] arg0,
                                       String arg1, SSLEngine arg2)
                throws java.security.cert.CertificateException
        {
            // TODO Auto-generated method stub
        }
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

彼岸花@开

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值