通过前面几篇的介绍,熟悉了CAS Server的运行和调试,这一篇演示一个实际的单点登录例子。
统一使用 SpringBoot+Meven 构建。
接下来,老夫就要开始飙车了。
一、配置hosts文件 1 2 3 4 # SSO单点登录Demo 127.0.0.1 cas.server.com 127.0.0.1 app1.com 127.0.0.1 app2.com
二、配置cas服务端 配置json服务注册
在src/main/resources/目录下创建services文件夹
在war包中,找到HTTPSandIMAPS-10000001.json文件,并copy到services文件夹中
将HTTPSandIMAPS-10000001.json文件内容修改如下
1 2 3 4 5 6 7 8 { "@class" : "org.apereo.cas.services.RegexRegisteredService" , "serviceId" : "^(https|http|imaps)://.*" , "name" : "HTTPS and HTTP and IMAPS" , "id" : 10000001 , "description" : "This service definition authorizes all application urls that support HTTPS and HTTP and IMAPS protocols." , "evaluationOrder" : 10000 }
默认cas支持https,不支持http客户端站点来登录,所以需要手动进行配置兼容。
如果不做此操作就会出现如下图:未认证授权的服务,所以还是乖乖的听话,跟着我的飙车轨道,别跑丢了。
体json文件中代表什么含义,后续篇会详细讲解到。
application.properties 配置
application.properties文件中,增加配置项cas.serviceRegistry.initFromJson=true表示开启了json注册服务。
另外为了Demo尽可能的简单方便理解,我这里启用静态账号密码方式认证(非JDBC和Rest)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 cas.authn.accept.users =test::test cas.serviceRegistry.initFromJson =true cas.logout.followServiceRedirects =true
三、配置客户端 客户端 app1 和 app2 的项目逻辑相同,不多说。
pom.xml 配置
添加 cas-client 包依赖,我用的 3.5.1 版本
1 2 3 4 5 6 7 8 9 <properties > <cas.client.version > 3.5.1</cas.client.version > </properties > <dependency > <groupId > org.jasig.cas.client</groupId > <artifactId > cas-client-core</artifactId > <version > ${java.cas.client.version}</version > </dependency >
源代码我已上传至GitHub,主要几个类如下。
CasClientConfig.java是CAS 客户端配置类,这个最重要。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 @Configuration public class CasClientConfig { @Bean public FilterRegistrationBean filterSingleRegistration () { FilterRegistrationBean registration = new FilterRegistrationBean (); registration.setFilter(new SingleSignOutFilter ()); registration.addUrlPatterns("/*" ); Map<String, String> initParameters = new HashMap (); initParameters.put("casServerUrlPrefix" , CasConfig.CAS_SERVER_LOGIN_PATH); registration.setInitParameters(initParameters); registration.setOrder(1 ); return registration; } @Bean public ServletListenerRegistrationBean<EventListener> singleSignOutListenerRegistration () { ServletListenerRegistrationBean<EventListener> registrationBean = new ServletListenerRegistrationBean <EventListener>(); registrationBean.setListener(new SingleSignOutHttpSessionListener ()); registrationBean.setOrder(1 ); return registrationBean; } @Bean public FilterRegistrationBean filterValidationRegistration () { FilterRegistrationBean registration = new FilterRegistrationBean (); registration.setFilter(new Cas30ProxyReceivingTicketValidationFilter ()); registration.addUrlPatterns("/*" ); Map<String, String> initParameters = new HashMap (); initParameters.put("casServerUrlPrefix" , CasConfig.CAS_SERVER_PATH); initParameters.put("serverName" , CasConfig.SERVER_NAME); initParameters.put("encodeServiceUrl" , "false" ); registration.setInitParameters(initParameters); registration.setOrder(1 ); return registration; } @Bean public FilterRegistrationBean filterAuthenticationRegistration () { FilterRegistrationBean registration = new FilterRegistrationBean (); Map<String, String> initParameters = new HashMap (); registration.setFilter(new AuthenticationFilter ()); registration.addUrlPatterns("/*" ); initParameters.put("casServerLoginUrl" , CasConfig.CAS_SERVER_LOGIN_PATH); initParameters.put("serverName" , CasConfig.SERVER_NAME); initParameters.put("ignorePattern" , ".*" ); initParameters.put("ignoreUrlPatternType" , "com.xncoding.cas.config.SimpleUrlPatternMatcherStrategy" ); registration.setInitParameters(initParameters); registration.setOrder(1 ); return registration; } @Bean public FilterRegistrationBean filterAssertionThreadLocalRegistration () { FilterRegistrationBean registration = new FilterRegistrationBean (); registration.setFilter(new AssertionThreadLocalFilter ()); registration.addUrlPatterns("/*" ); registration.setOrder(1 ); return registration; } @Bean public FilterRegistrationBean filterWrapperRegistration () { FilterRegistrationBean registration = new FilterRegistrationBean (); registration.setFilter(new HttpServletRequestWrapperFilter ()); registration.addUrlPatterns("/*" ); registration.setOrder(1 ); return registration; } }
URL拦截匹配规则类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public class SimpleUrlPatternMatcherStrategy implements UrlPatternMatcherStrategy { @Override public boolean matches (String url) { if (url.contains("/logout" )) { return true ; } List<String> list = Arrays.asList( "/" , "/index" , "/favicon.ico" ); String name = url.substring(url.lastIndexOf("/" )); if (name.contains("?" )) { name = name.substring(0 , name.indexOf("?" )); } System.out.println("name:" + name); boolean result = list.contains(name); if (!result) { System.out.println("拦截URL:" + url); } return result; } @Override public void setPattern (String pattern) { } }
Cas的一些配置项,这里我写成常量了,其实作为Properties配置文件才是最好:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class CasConfig { public static String SERVER_NAME = "http://app1.com:8080/" ; public static String APP_LOGOUT_PATH = SERVER_NAME + "user/logout/success" ; public static String CAS_SERVER_PATH = "https://cas.server.com:8443/cas" ; public static String CAS_SERVER_LOGIN_PATH = "https://cas.server.com:8443/cas/login" ; public static String CAS_SERVER_LOGOUT_PATH = "https://cas.server.com:8443/cas/logout" ; }
然后就是几个Controller接口类,定义的/books是需要登录才能访问,而/index不需要拦截。这里就不贴了。
几个常用配置项
cas.logout.followServiceRedirects=true 是否允许客户端Logout后重定向到service参数指定的资源
tgt.maxTimeToLiveInSeconds=28800 指定Session的最大有效时间,即从生成到指定时间后就将超时,默认28800s,即8小时
tgt.timeToKillInSeconds=7200 指定用户操作的超时时间,即用户在多久不操作后就超时,默认7200s,即2小时。
还要注意客户端web.xml配置的超时时间,即只有客户端配置超时时间不大于tgt.timeToKillInSeconds时才能看见服务端设置的效果
st.timeToKillInSeconds=10 指定service ticket的有效时间,默认10s,
这也是debug追踪CAS应用认证过程中经常会失败的原因,因为追踪的时候service ticket已经过了10秒有效期了
slo.callbacks.disabled=false 是否禁用单点登出