首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

yale-cas 与 shiro开展整合

2014-01-22 
yale-cas 与 shiro进行整合注意:cas-client.version 3.2.1版本。3.3.0版本单点登出存在问题,还在研究。一.普

yale-cas 与 shiro进行整合

注意:cas-client.version 3.2.1版本。3.3.0版本单点登出存在问题,还在研究。

一.普通的CAS客户端整合1.CAS与客户端直接整合

参考资料:Configuring the Jasig CAS Client for Java in the web.xml

相关配置直接写在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>    <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>    <!-- 使用的是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>    <!-- 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>    <!-- 该过滤器负责实现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在展示项目中存在

?

?

2.CAS通过Spring整合到项目中

参考资料 Configuring the JA-SIG CAS Client for Java using Spring

与上面一种差不多,只不过在web.xml中filter通过spring的DelegatingFilterProxy进行代理,另外需要注意的是bean ticketValidationFilter的属性p:redirectAfterValidation="true"是单点登出的关键

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 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></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" />    <bean name="authenticationFilter" p:renew="false"        p:gateway="false" p:casServerLoginUrl="${cas.server.login.url}" p:serverName="${server.name}" />    <bean name="ticketValidationFilter" p:serverName="${server.name}">        <property name="ticketValidator">            <bean value="${cas.server.url}" />            </bean>        </property>    </bean>    <bean name="httpServletRequestWrapperFilter" />    <bean name="assertionThreadLocalFilter" /></beans>

?

配置文件cas.properties

?

cas.server.url=https://sso.gqshao.com:8443/cascas.server.login.url=https://sso.gqshao.com:8443/cas/login#Client Addressserver.name=http://sso.gqshao.com:8090
?

?

?

二.CAS与Shiro进行整合

Shiro的使用,请参考我博客中《简单的Spring整合Shiro》

?

注意,这里没有采用shiro提供的shiro-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>?

?

2 配置文件applicationContext-cas-shiro.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" 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" />    <bean name="ticketValidationFilter" p:serverName="${server.name}" >        <property name="ticketValidator">            <bean p:encoding="UTF-8">                <constructor-arg index="0" value="${cas.server.url}" />            </bean>        </property>    </bean>        <bean name="authenticationFilter" p:renew="false"        p:gateway="false" p:casServerLoginUrl="${cas.server.login.url}" p:serverName="${server.name}" />    <bean name="httpServletRequestWrapperFilter" />    <bean name="assertionThreadLocalFilter" />    <!-- Shiro -->    <!-- Shiro's main business-tier object for web-enabled applications -->    <bean id="securityManager" ref="shiroDbRealm" />        <property name="cacheManager" ref="shiroEhcacheManager" />    </bean>    <!-- 項目自定义的Realm -->    <bean id="shiroDbRealm" />    <!-- Shiro Filter -->    <bean id="shiroFilter" ref="securityManager" />        <!-- 用于调用Controller -->        <property name="loginUrl" value="/login" />        <property name="successUrl" value="/" />        <!-- 自己实现的formAuthcFilter,加入key type -->        <property name="filters">            <util:map>                <entry key="authc">                    <bean />                </entry>            </util:map>        </property>        <property name="filterChainDefinitions">            <value>                /login = authc                /logout = logout                /static/** = anon                /** = user            </value>        </property>    </bean>    <!-- 用户授权信息Cache, 采用EhCache -->    <bean id="shiroEhcacheManager" value="classpath:security/ehcache-shiro.xml" />    </bean>    <!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->    <bean id="lifecycleBeanPostProcessor" />    <!-- AOP式方法级权限检查 -->    <bean depends-on="lifecycleBeanPostProcessor">        <property name="proxyTargetClass" value="true" />    </bean>    <bean ref="securityManager" />    </bean></beans>
?

?

?

3.实现类

(1)一个继承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;    }}

?

4.流程说明 和 注意事项

(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。

热点排行