參考資料:Configuring the Jasig CAS Client for Java in the web.xmlhtml
相關配置直接寫在web.xml文件中java
內容以下:web
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> <servlet> <servlet-name>cas oss info</servlet-name> <servlet-class>com.gqshao.cas.servlet.InfoServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>cas oss info</servlet-name> <url-pattern>/info</url-pattern> </servlet-mapping> <servlet> <servlet-name>cas oss logout</servlet-name> <servlet-class>com.gqshao.cas.servlet.LogoutServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>cas oss logout</servlet-name> <url-pattern>/logout</url-pattern> </servlet-mapping> <!-- 用於單點退出,該過濾器用於實現單點登出功能,可選配置 The SingleSignOutFilter can affect character encoding. --> <listener> <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class> </listener> <!-- Filter 定義 --> <!-- Character Encoding filter --> <filter> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> </filter-mapping> <!-- https://wiki.jasig.org/display/CASC/Configuring+Single+Sign+Out --> <!-- 該過濾器用於實現單點登出功能,可選配置。 --> <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> <!-- https://wiki.jasig.org/display/CASC/Configuring+the+Jasig+CAS+Client+for+Java+in+the+web.xml --> <!-- AuthenticationFilter是檢測是否須要經過身份驗證的用戶。若是一個用戶須要身份驗證,它將用戶重定向到CAS服務器。 --> <filter> <filter-name>CAS Authentication Filter</filter-name> <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class> <init-param> <param-name>casServerLoginUrl</param-name> <param-value>https://sso.gqshao.com:8443/cas/login</param-value> </init-param> <init-param> <!--這裏的server是服務端的IP --> <param-name>serverName</param-name> <param-value>http://sso.gqshao.com</param-value> </init-param> <init-param> <param-name>renew</param-name> <param-value>false</param-value> </init-param> </filter> <filter-mapping> <filter-name>CAS Authentication Filter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> </filter-mapping> <!-- 使用的是Cas20ProxyReceivingTicketValidationFilter 驗證使用CAS2.0協議的門票 --> <!-- 根據CAS文檔描述:If you are using proxy validation, you should map the validation filter before the authentication filter. --> <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://sso.gqshao.com:8443/cas</param-value> </init-param> <init-param> <param-name>serverName</param-name> <param-value>http://sso.gqshao.com</param-value> </init-param> <init-param> <param-name>useSession</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>CAS Validation Filter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> </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> <!-- ======================== 單點登陸結束 ======================== --> <welcome-file-list> <welcome-file>WEB-INF/views/index.jsp</welcome-file> </welcome-file-list> </web-app>
這裏面用到一個JSP和兩個Servlet在展現項目中存在spring
參考資料 Configuring the JA-SIG CAS Client for Java using Springapache
與上面一種差很少,只不過在web.xml中filter經過spring的DelegatingFilterProxy進行代理,另外須要注意的是bean ticketValidationFilter的屬性p:redirectAfterValidation="true"是單點登出的關鍵spring-mvc
web.xml緩存
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> <context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath*:/applicationContext-cas.xml </param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- https://wiki.jasig.org/display/CASC/Configuring+Single+Sign+Out --> <!-- 用於單點退出,該過濾器用於實現單點登出功能,可選配置 The SingleSignOutFilter can affect character encoding. --> <listener> <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class> </listener> <servlet> <servlet-name>springServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <servlet> <servlet-name>cas oss info</servlet-name> <servlet-class>com.gqshao.cas.servlet.InfoServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>cas oss info</servlet-name> <url-pattern>/info</url-pattern> </servlet-mapping> <servlet> <servlet-name>cas oss logout</servlet-name> <servlet-class>com.gqshao.cas.servlet.LogoutServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>cas oss logout</servlet-name> <url-pattern>/logout</url-pattern> </servlet-mapping> <!-- Filter 定義 --> <!-- Character Encoding filter --> <filter> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> </filter-mapping> <!-- CAS --> <filter> <filter-name>CAS Single Sign Out Filter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <param-name>targetBeanName</param-name> <param-value>singleSignOutFilter</param-value> </init-param> </filter> <filter-mapping> <filter-name>CAS Single Sign Out Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter> <filter-name>CAS Authentication Filter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <param-name>targetBeanName</param-name> <param-value>authenticationFilter</param-value> </init-param> </filter> <filter-mapping> <filter-name>CAS Authentication Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter> <filter-name>CAS Validation Filter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <param-name>targetBeanName</param-name> <param-value>ticketValidationFilter</param-value> </init-param> </filter> <filter-mapping> <filter-name>CAS Validation Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter> <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <param-name>targetBeanName</param-name> <param-value>httpServletRequestWrapperFilter</param-value> </init-param> </filter> <filter-mapping> <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter> <filter-name>CAS Assertion Thread Local Filter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <param-name>targetBeanName</param-name> <param-value>assertionThreadLocalFilter</param-value> </init-param> </filter> <filter-mapping> <filter-name>CAS Assertion Thread Local Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
/src/main/resources/applicationContext-cas.xml服務器
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:sec="http://www.springframework.org/schema/security" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd"> <!-- 讀取配置文件 --> <context:property-placeholder location="classpath*:cas.properties" ignore-unresolvable="true" /> <bean name="singleSignOutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter" /> <bean name="authenticationFilter" class="org.jasig.cas.client.authentication.AuthenticationFilter" p:renew="false" p:gateway="false" p:casServerLoginUrl="${cas.server.login.url}" p:serverName="${server.name}" /> <bean name="ticketValidationFilter" class="org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter" p:redirectAfterValidation="true" p:serverName="${server.name}"> <property name="ticketValidator"> <bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator"> <constructor-arg index="0" value="${cas.server.url}" /> </bean> </property> </bean> <bean name="httpServletRequestWrapperFilter" class="org.jasig.cas.client.util.HttpServletRequestWrapperFilter" /> <bean name="assertionThreadLocalFilter" class="org.jasig.cas.client.util.AssertionThreadLocalFilter" /> </beans>
配置文件cas.propertiessession
cas.server.url=https://sso.gqshao.com:8443/cas cas.server.login.url=https://sso.gqshao.com:8443/cas/login #Client Address server.name=http://sso.gqshao.com:8090
Shiro的使用,請參考我博客中《簡單的Spring整合Shiro》mvc
注意,這裏沒有采用shiro提供的shiro-cas依賴,一樣也沒有使用到org.apache.shiro.cas.CasFilter,但本身實現的CustomFormAuthenticationFilter參考了CasFilter
首先web.xml中分別進行cas和shiro的filter的配置,須要注意的是filter的位置關係。
另外要注意的是cas登錄認證以後返回的地址
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> <context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath*:/applicationContext-cas-shiro.xml </param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- https://wiki.jasig.org/display/CASC/Configuring+Single+Sign+Out --> <!-- 用於單點退出,該過濾器用於實現單點登出功能,可選配置 The SingleSignOutFilter can affect character encoding. --> <listener> <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class> </listener> <servlet> <servlet-name>springServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <servlet> <servlet-name>cas oss info</servlet-name> <servlet-class>com.gqshao.cas.servlet.InfoServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>cas oss info</servlet-name> <url-pattern>/info</url-pattern> </servlet-mapping> <!-- Filter 定義 --> <!-- Character Encoding filter --> <filter> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> </filter-mapping> <!-- CAS --> <filter> <filter-name>CAS Single Sign Out Filter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <param-name>targetBeanName</param-name> <param-value>singleSignOutFilter</param-value> </init-param> </filter> <filter-mapping> <filter-name>CAS Single Sign Out Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter> <filter-name>CAS Validation Filter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <param-name>targetBeanName</param-name> <param-value>ticketValidationFilter</param-value> </init-param> </filter> <filter-mapping> <filter-name>CAS Validation Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter> <filter-name>CAS Authentication Filter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <param-name>targetBeanName</param-name> <param-value>authenticationFilter</param-value> </init-param> </filter> <filter-mapping> <filter-name>CAS Authentication Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter> <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <param-name>targetBeanName</param-name> <param-value>httpServletRequestWrapperFilter</param-value> </init-param> </filter> <filter-mapping> <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter> <filter-name>CAS Assertion Thread Local Filter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <param-name>targetBeanName</param-name> <param-value>assertionThreadLocalFilter</param-value> </init-param> </filter> <filter-mapping> <filter-name>CAS Assertion Thread Local Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- Shiro Security filter --> <filter> <filter-name>shiroFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>shiroFilter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> </filter-mapping> <session-config> <session-timeout>60</session-timeout> </session-config> </web-app>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:sec="http://www.springframework.org/schema/security" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd"> <!-- 讀取配置文件 --> <context:property-placeholder location="classpath*:cas.properties" ignore-unresolvable="true" /> <bean name="singleSignOutFilter" class="org.jasig.cas.client.session.SingleSignOutFilter" /> <bean name="ticketValidationFilter" class="org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter" p:redirectAfterValidation="true" p:serverName="${server.name}" > <property name="ticketValidator"> <bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator" p:encoding="UTF-8"> <constructor-arg index="0" value="${cas.server.url}" /> </bean> </property> </bean> <bean name="authenticationFilter" class="org.jasig.cas.client.authentication.AuthenticationFilter" p:renew="false" p:gateway="false" p:casServerLoginUrl="${cas.server.login.url}" p:serverName="${server.name}" /> <bean name="httpServletRequestWrapperFilter" class="org.jasig.cas.client.util.HttpServletRequestWrapperFilter" /> <bean name="assertionThreadLocalFilter" class="org.jasig.cas.client.util.AssertionThreadLocalFilter" /> <!-- Shiro --> <!-- Shiro's main business-tier object for web-enabled applications --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="shiroDbRealm" /> <property name="cacheManager" ref="shiroEhcacheManager" /> </bean> <!-- 項目自定義的Realm --> <bean id="shiroDbRealm" class="com.gqshao.cas.authentication.ShiroDbRealm" /> <!-- Shiro Filter --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager" /> <!-- 用於調用Controller --> <property name="loginUrl" value="/login" /> <property name="successUrl" value="/" /> <!-- 本身實現的formAuthcFilter,加入key type --> <property name="filters"> <util:map> <entry key="authc"> <bean class="com.gqshao.cas.authentication.CustomFormAuthenticationFilter" /> </entry> </util:map> </property> <property name="filterChainDefinitions"> <value> /login = authc /logout = logout /static/** = anon /** = user </value> </property> </bean> <!-- 用戶受權信息Cache, 採用EhCache --> <bean id="shiroEhcacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> <property name="cacheManagerConfigFile" value="classpath:security/ehcache-shiro.xml" /> </bean> <!-- 保證明現了Shiro內部lifecycle函數的bean執行 --> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" /> <!-- AOP式方法級權限檢查 --> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"> <property name="proxyTargetClass" value="true" /> </bean> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager" /> </bean> </beans>
(1)一個繼承org.apache.shiro.web.filter.authc.AuthenticatingFilter的實現類
package com.gqshao.cas.authentication; import java.util.Map; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.subject.Subject; import org.apache.shiro.web.filter.authc.AuthenticatingFilter; import org.apache.shiro.web.util.WebUtils; import org.jasig.cas.client.authentication.AttributePrincipal; import org.jasig.cas.client.util.AssertionHolder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class CustomFormAuthenticationFilter extends AuthenticatingFilter { private static final Logger log = LoggerFactory.getLogger(CustomFormAuthenticationFilter.class); public static final String DEFAULT_ERROR_KEY_ATTRIBUTE_NAME = "shiroLoginFailure"; public static final String DEFAULT_LOGINNAME_PARAM = "loginName"; public static final String DEFAULT_PASSWORD_PARAM = "password"; public static final String DEFAULT_REMEMBER_ME_PARAM = "rememberMe"; // 自定義的輸入字段 public static final String DEFAULT_CUSTOM_PARAM = "custom"; private String loginNameParam = DEFAULT_LOGINNAME_PARAM; private String passwordParam = DEFAULT_PASSWORD_PARAM; private String rememberMeParam = DEFAULT_REMEMBER_ME_PARAM; private String customParam = DEFAULT_CUSTOM_PARAM; private String failureKeyAttribute = DEFAULT_ERROR_KEY_ATTRIBUTE_NAME; public CustomFormAuthenticationFilter() { setLoginUrl(DEFAULT_LOGIN_URL); } @Override public void setLoginUrl(String loginUrl) { String previous = getLoginUrl(); if (previous != null) { this.appliedPaths.remove(previous); } super.setLoginUrl(loginUrl); if (log.isTraceEnabled()) { log.trace("Adding login url to applied paths."); } this.appliedPaths.put(getLoginUrl(), null); } /** * 在訪問被拒絕後執行 */ @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { return executeLogin(request, response); } /** * 建立自定義的令牌 */ @Override protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) { if (request instanceof HttpServletRequest) { HttpServletRequest httpRequest = (HttpServletRequest) request; AttributePrincipal principal = (AttributePrincipal) httpRequest.getUserPrincipal(); if (principal == null) { return null; } CustomToken token = new CustomToken(); Map<String, Object> attrs = principal.getAttributes(); token.setLoginName(attrs.get("loginname").toString()); token.setPassword(attrs.get("password").toString()); token.setSalt(attrs.get("salt").toString()); token.setCustom(attrs.get("custom").toString()); token.setHost(getHost(request)); return token; } return null; } protected boolean isLoginSubmission(ServletRequest request, ServletResponse response) { return (request instanceof HttpServletRequest) && WebUtils.toHttp(request).getMethod().equalsIgnoreCase(POST_METHOD); } protected boolean isRememberMe(ServletRequest request) { return false; } protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception { issueSuccessRedirect(request, response); return false; } protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) { setFailureAttribute(request, e); return true; } protected void setFailureAttribute(ServletRequest request, AuthenticationException ae) { String className = ae.getClass().getName(); request.setAttribute(getFailureKeyAttribute(), className); } public String getFailureKeyAttribute() { return failureKeyAttribute; } public void setFailureKeyAttribute(String failureKeyAttribute) { this.failureKeyAttribute = failureKeyAttribute; } }
(2)Token
package com.gqshao.cas.authentication; import org.apache.commons.lang3.StringUtils; import org.apache.shiro.authc.HostAuthenticationToken; import org.apache.shiro.authc.RememberMeAuthenticationToken; public class CustomToken implements HostAuthenticationToken, RememberMeAuthenticationToken { private String loginName; private String password; private String host; private boolean rememberMe = false; private String custom; private String salt; public CustomToken() { } public CustomToken(String loginName, String password, String salt, boolean rememberMe, String host, String custom) { this.loginName = loginName; this.password = password; this.setSalt(salt); this.rememberMe = rememberMe; this.host = host; this.custom = custom; } public Object getPrincipal() { return getLoginName(); } public Object getCredentials() { return getPassword(); } public String getHost() { return host; } public boolean isRememberMe() { return rememberMe; } public void clear() { this.loginName = null; this.host = null; this.password = null; this.rememberMe = false; this.custom = null; this.setSalt(null); } public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getName()); sb.append(" - "); sb.append(loginName); sb.append(", rememberMe=").append(rememberMe); if (StringUtils.isNotBlank(host)) { sb.append(" (").append(host).append(")"); } return sb.toString(); } public String getLoginName() { return loginName; } public void setLoginName(String loginName) { this.loginName = loginName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getCustom() { return custom; } public void setCustom(String custom) { this.custom = custom; } public void setHost(String host) { this.host = host; } public void setRememberMe(boolean rememberMe) { this.rememberMe = rememberMe; } public String getSalt() { return salt; } public void setSalt(String salt) { this.salt = salt; } }
(3)AuthorizingRealm的實現類,這裏實際上是爲了封裝principal(ShiroUser),經過Shiro保存到Session中,後續能夠經過SecurityUtils.getSubject().getPrincipal()隨時調用,真正的登錄認證經過CAS已經完成。
package com.gqshao.cas.authentication; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import com.gqshao.cas.domain.ShiroUser; public class ShiroDbRealm extends AuthorizingRealm { private Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired public ShiroDbRealm() { super(); setAuthenticationTokenClass(CustomToken.class); } /** * 認證回調函數,登陸時調用. */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException { CustomToken token = (CustomToken) authcToken; ShiroUser root = new ShiroUser(); // TODO: 經過Token與本系統RBAC關聯起來 root.setId("本身實現"); root.setLoginName(token.getLoginName()); root.setPassword(token.getPassword()); root.setSalt(token.getSalt()); root.setCustom(token.getCustom()); logger.info("用戶[{}]登錄系統, IP:[{}]", token.getLoginName(),token.getHost()); return new SimpleAuthenticationInfo(root, token.getPassword(), getName()); } /** * 受權查詢回調函數, 進行鑑權但緩存中無用戶的受權信息時調用. */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { // TODO: 實現鑑權 return null; } }
(1)登錄系統的時候,首先由CAS攔截,而後再CAS服務器端登錄認證;
(2)由於Shiro也須要登錄認證,因此CAS認證經過後,請求會被CustomFormAuthenticationFilter攔截,並調用方法onAccessDenied,此時開始走Shiro認證;
(3)首先會在CustomFormAuthenticationFilter的createToken中組裝Token。這時能夠經過調用httpRequest.getUserPrincipal()或AssertionHolder.getAssertion().getPrincipal();拿到CAS返回信息封裝的principal。經過解析principal,組裝Token,由於Token能夠實現定製,因此這裏按需求實現;
(4)Token組裝後,會調用ShiroDbRealm的doGetAuthenticationInfo方法進行登陸認證,由於實際的登錄認證已經在CAS服務器端實現,因此這裏主要是爲了shiro的principal,返回SimpleAuthenticationInfo,而且不要寫initCredentialsMatcher方法。
1.CAS的principal(principal解釋)能夠經過配置相應Filter後,經過httpRequest.getUserPrincipal(), 或AssertionHolder.getAssertion().getPrincipal()獲得;
2.Shiro的能夠經過SecurityUtils.getSubject().getPrincipal()獲得;
3.當Shiro登錄認證以後,經過httpRequest.getUserPrincipal()獲得CAS Principal的方法不能夠在用;緣由是此時經過httpRequest.getUserPrincipal()調用返回的是org.apache.shiro.web.servlet.ShiroHttpServletRequest.ObjectPrincipal,而且這是一個私有類,而且實現了java.security.Principal。
轉載請註明 : http://sgq0085.iteye.com/blog/2003783
4.爲了傳遞中文參數,須要注意兩個地方,一個是服務器端的/webapp/WEB-INF/view/jsp/protocol/2.0/casServiceValidationSuccess.jsp頁面,須要設置爲<%@ page session="false" language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>;另外一個地方是客戶端的配置文件中Cas20ServiceTicketValidator的encoding屬性也要設置爲UTF-8。