单点登录CAS系列8-客户端配置单点登出 - 将cas中web.xml配置到properties中

原理

本文内容包括配置单点登出、登出后自动跳转指定资源、CASServer禁用单点登出等

与单点登录相对应:登录的地址是/login,登出的地址是/logout,这里需要配置下面两个Filter

SingleSignOutFilter:用来使Session失效(要配置casServerUrlPrefix参数)

SingleSignOutHttpSessionListener:用于在Session过期时移除其对应的映射关系

  • 登出后自动跳转指定资源

登出后默认会跳转到CASServer的登出页,若想跳转到其它资源,可在/logout的URL后面加上service=jumpurl

比如http://sso.jadyer.com:8080/cas-server-web/logout?service=https://jadyer.github.io/

但默认servcie跳转不会生效,需要CASServer配置//WEB-INF//cas.properties中的cas.logout.followServiceRedirects=true

另外为org.jasig.cas.client.session.SingleSignOutFilter增加service参数是没用的,因为登出后跳转到指定资源属于服务端行为

  • 禁用单点登出

设置//CASServer//WEB-INF//cas.properties中的slo.callbacks.disabled=true

测试时点击登出后虽然页面跳转到了默认登出页,但再次访问CASClient资源发现并没有登出,即单点登出禁用成功

  • 测试单点登出

测试时先登出,然后在浏览器新标签页访问CASClient资源,发现会被自动重定向到单点登录页

或登出后,再点浏览器后退按钮,发现会后退到之前的资源页,但在这个页面点击任何请求,都会自动重定向到单点登录页

代码

下面是客户端的web.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
  3. <context-param>
  4. <param-name>contextConfigLocation</param-name>
  5. <param-value>classpath:applicationContext.xml</param-value>
  6. </context-param>
  7. <listener>
  8. <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  9. </listener>
  10. <servlet>
  11. <servlet-name>SpringMVC</servlet-name>
  12. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  13. <init-param>
  14. <param-name>contextConfigLocation</param-name>
  15. <param-value>classpath:applicationContext.xml</param-value>
  16. </init-param>
  17. </servlet>
  18. <servlet-mapping>
  19. <servlet-name>SpringMVC</servlet-name>
  20. <url-pattern>/</url-pattern>
  21. </servlet-mapping>
  22. <filter>
  23. <filter-name>CharacterEncodingFilter</filter-name>
  24. <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
  25. <init-param>
  26. <param-name>encoding</param-name>
  27. <param-value>UTF-8</param-value>
  28. </init-param>
  29. <init-param>
  30. <param-name>forceEncoding</param-name>
  31. <param-value>true</param-value>
  32. </init-param>
  33. </filter>
  34. <filter-mapping>
  35. <filter-name>CharacterEncodingFilter</filter-name>
  36. <url-pattern>/*</url-pattern>
  37. </filter-mapping>
  38. <!-- SSO -->
  39. <listener>
  40. <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>
  41. </listener>
  42. <filter>
  43. <filter-name>casSingleSignOutFilter</filter-name>
  44. <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
  45. </filter>
  46. <filter-mapping>
  47. <filter-name>casSingleSignOutFilter</filter-name>
  48. <url-pattern>/*</url-pattern>
  49. </filter-mapping>
  50. <filter>
  51. <filter-name>casAuthenticationFilter</filter-name>
  52. <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
  53. </filter>
  54. <filter-mapping>
  55. <filter-name>casAuthenticationFilter</filter-name>
  56. <url-pattern>/*</url-pattern>
  57. </filter-mapping>
  58. <filter>
  59. <filter-name>casTicketValidationFilter</filter-name>
  60. <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
  61. </filter>
  62. <filter-mapping>
  63. <filter-name>casTicketValidationFilter</filter-name>
  64. <url-pattern>/*</url-pattern>
  65. </filter-mapping>
  66. <filter>
  67. <filter-name>casHttpServletRequestWrapperFilter</filter-name>
  68. <filter-class>org.jasig.cas.client.util.HttpServletRequestWrapperFilter</filter-class>
  69. </filter>
  70. <filter-mapping>
  71. <filter-name>casHttpServletRequestWrapperFilter</filter-name>
  72. <url-pattern>/*</url-pattern>
  73. </filter-mapping>
  74. <filter>
  75. <filter-name>casAssertionThreadLocalFilter</filter-name>
  76. <filter-class>org.jasig.cas.client.util.AssertionThreadLocalFilter</filter-class>
  77. </filter>
  78. <filter-mapping>
  79. <filter-name>casAssertionThreadLocalFilter</filter-name>
  80. <url-pattern>/*</url-pattern>
  81. </filter-mapping>
  82. </web-app>


下面是客户端的/src/applicationContext.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
  3. <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
  4. <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
  5. <property name="ignoreResourceNotFound" value="false"/>
  6. <property name="locations">
  7. <list>
  8. <value>classpath:config.properties</value>
  9. </list>
  10. </property>
  11. </bean>
  12. <mvc:resources mapping="/index.jsp" location="/index.jsp"/>
  13. <!-- cas -->
  14. <bean name="casSingleSignOutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter">
  15. <property name="casServerUrlPrefix" value="${casServerUrlPrefix}"/>
  16. </bean>
  17. <bean name="casAuthenticationFilter" class="org.jasig.cas.client.authentication.AuthenticationFilter">
  18. <property name="serverName" value="${casClientServerName}"/>
  19. <property name="casServerLoginUrl" value="${casServerLoginUrl}"/>
  20. </bean>
  21. <bean name="casTicketValidationFilter" class="org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter">
  22. <property name="serverName" value="${casClientServerName}"/>
  23. <property name="ticketValidator">
  24. <bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
  25. <constructor-arg index="0" value="${casServerUrlPrefix}"/>
  26. </bean>
  27. </property>
  28. </bean>
  29. </beans>


下面是/src/config.properties

  1. #&lt;&lt;Central Authentication Service&gt;&gt;
  2. #where to logout
  3. casServerLogoutUrl=http://sso.jadyer.com:8080/cas-server-web/logout
  4. #where to login
  5. casServerLoginUrl=http://sso.jadyer.com:8080/cas-server-web/login
  6. #login server root
  7. casServerUrlPrefix=http://sso.jadyer.com:8080/cas-server-web
  8. #who am i
  9. casClientServerName=http://boss.jadyer.com:8080


下面是/WebRoot/index.jsp

  1. <%@ page pageEncoding="UTF-8"%>
  2. <%@ page import="java.util.Map"%>
  3. <%@ page import="java.net.URLDecoder"%>
  4. <%@ page import="com.jadyer.util.ConfigUtil"%>
  5. <%@ page import="org.jasig.cas.client.util.AssertionHolder"%>
  6. <%@ page import="org.jasig.cas.client.authentication.AttributePrincipal"%>
  7. <script>
  8. function ssoLogout(){
  9. if(confirm('确定要退出系统吗?')){
  10. //top.location.href ='http://sso.jadyer.com:8080/cas-server-web/logout?service=https://jadyer.github.io/';
  11. //top.location.href ='http://sso.jadyer.com:8080/cas-server-web/logout?service=http://sso.jadyer.com:8080/cas-server-web/login';
  12. top.location.href ='<%=ConfigUtil.INSTANCE.getProperty("casServerLogoutUrl")%>';
  13. }
  14. }
  15. </script>
  16. <body style="background-color:#CBE0C9;">
  17. <span style="color:red; font-size:32px; font-weight:bold;">客户端登录成功</span>
  18. <br>
  19. <br>
  20. <a href="javascript:ssoLogout();">我要登出</a>
  21. </body>
  22. <hr size="2">
  23. <%
  24. AttributePrincipal principal = (AttributePrincipal)request.getUserPrincipal();
  25. Map<String, Object> attributes = principal.getAttributes();
  26. out.print("principal.getName()=" + principal.getName() + "<br/>");
  27. out.print("request.getRemoteUser()=" + request.getRemoteUser() + "<br/>");
  28. out.print("登录用户:" + attributes.get("userId") + "<br/>");
  29. out.print("登录时间:" + AssertionHolder.getAssertion().getAuthenticationDate() + "<br/>");
  30. out.print("-----------------------------------------------------------------------<br/>");
  31. for(Map.Entry<String, Object> entry : attributes.entrySet()){
  32. //服务端返回中文时需要encode,客户端接收显示中文时需要decode,否则会乱码
  33. out.print(entry.getKey() + "=" + URLDecoder.decode(entry.getValue().toString(), "UTF-8") + "<br/>");
  34. }
  35. out.print("-----------------------------------------------------------------------<br/>");
  36. Map<String, Object> attributes22 = AssertionHolder.getAssertion().getAttributes();
  37. for(Map.Entry<String, Object> entry : attributes22.entrySet()){
  38. out.print(entry.getKey() + "=" + entry.getValue() + "<br/>");
  39. }
  40. out.print("-----------------------------------------------------------------------<br/>");
  41. Map<String, Object> attributes33 = AssertionHolder.getAssertion().getPrincipal().getAttributes();
  42. for(Map.Entry<String, Object> entry : attributes33.entrySet()){
  43. out.print(entry.getKey() + "=" + entry.getValue() + "<br/>");
  44. }
  45. %>


最后是读取配置文件的工具类ConfigUtil.java

  1. package com.jadyer.util;
  2. import java.io.IOException;
  3. import java.util.Properties;
  4. import java.util.regex.Pattern;
  5. /**
  6. * 配置文件读取工具类
  7. * --------------------------------------------------------------------------------------------------------------------
  8. * 用法为ConfigUtil.INSTANCE.getProperty("KJJF.databaseURL")
  9. * 采用枚举的方式,也是Effective Java作者Josh Bloch提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象
  10. * --------------------------------------------------------------------------------------------------------------------
  11. * @version v2.1
  12. * @history v2.1-->增加<code>getPropertyBySysKey()</code>方法,用于获取配置文件的键值中含系统属性时的值,详见该方法注释
  13. * @history v2.0-->采用枚举的方式实现单例
  14. * @history v1.0-->通过内部类实现单例
  15. * Created by 玄玉<https://jadyer.github.io/> on 2012/06/07 17:30.
  16. */
  17. public enum ConfigUtil {
  18. INSTANCE;
  19. private Properties config;
  20. private ConfigUtil(){
  21. config = new Properties();
  22. try {
  23. config.load(ConfigUtil.class.getResourceAsStream("/config.properties"));
  24. System.out.println("Load /config.properties SUCCESS...");
  25. } catch (IOException e) {
  26. System.out.println("Load /config.properties Error...");
  27. e.printStackTrace();
  28. throw new ExceptionInInitializerError("加载系统配置文件失败...");
  29. }
  30. }
  31. public String getProperty(String key){
  32. return config.getProperty(key);
  33. }
  34. public int getPropertyForInt(String key){
  35. return Integer.parseInt(config.getProperty(key));
  36. }
  37. /**
  38. * 配置文件的键值中含系统属性时的获取方式
  39. * -------------------------------------------------------------------------
  40. * 若配置文件的某个键值含类似于${user.dir}的写法,如log=${user.dir}/app.log
  41. * 则可以通过该方法使用系统属性中user.dir的值,替换掉配置文件键值中的${user.dir}
  42. * -------------------------------------------------------------------------
  43. * Created by 玄玉<https://jadyer.github.io/> on 2015/02/02 17:22.
  44. */
  45. public String getPropertyBySysKey(String key){
  46. String value = config.getProperty(key);
  47. if(null!=value && Pattern.compile("\\$\\{\\w+(\\.\\w+)*\\}").matcher(value).find()){
  48. String sysKey = value.substring(value.indexOf("${")+2, value.indexOf("}"));
  49. value = value.replace("${"+sysKey+"}", System.getProperty(sysKey));
  50. }
  51. return value;
  52. }
  53. }

展开阅读全文

没有更多推荐了,返回首页