准备工作
cas单点登录开始前准备,请参考cas4.2.7实现单点登录。
与shiro进行整合
注:准备工作的基础上,对cas客户端进行如下改进。
引入相关jar包
shiro-cas-1.2.6.jar
shiro-core-1.2.6.jar
shiro-spring-1.2.6.jar
shiro-web-1.2.6.jar
web.xml引入shiro过滤器
<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" 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_3_0.xsd"> <display-name>Archetype Created Web Application</display-name> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:spring-web.xml, classpath:spring-shiro.xml </param-value> </context-param> <!-- Shiro配置 --> <filter> <filter-name>shiroFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <param-name>targetFilterLifecycle</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>shiroFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- ****************** 单点登录开始 ********************--> <!-- 用于实现单点登出功能 可选 --> <listener> <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class> </listener> <!-- 该过滤器用于实现单点登出功能,单点退出配置,一定要放在其他filter之前 可选 --> <filter> <filter-name>CAS Single Sign Out Filter</filter-name> <filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class> <init-param> <param-name>casServerUrlPrefix</param-name> <param-value>http://127.0.0.1:8080/cas-web/</param-value> </init-param> </filter> <filter-mapping> <filter-name>CAS Single Sign Out Filter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 该过滤器对HttpServletRequest请求包装, 可通过HttpServletRequest的getRemoteUser()方法获得登录用户的登录名,可选 --> <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()。 这个类把Assertion信息放在ThreadLocal变量中,这样应用程序不在web层也能够获取到当前登录信息 --> <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> <!-- ****************** 单点登录结束 ********************--> <servlet> <servlet-name>springMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-web.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>
引入shiro的配置文件
<?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:property-placeholder location="classpath:shiro.properties" ignore-unresolvable="true"/> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager"/> <!-- 设定角色的登录链接,这里为cas登录页面的链接可配置回调地址 --> <property name="loginUrl" value="${cas.loginUrl}" /> <property name="successUrl" value="${shiro.successUrl}" /> <property name="filters"> <map> <entry key="casFilter" value-ref="casFilter"/> </map> </property> <property name="filterChainDefinitions"> <value> /shiro-cas = casFilter /** = authc </value> </property> </bean> <bean id="casFilter" class="org.apache.shiro.cas.CasFilter"> <property name="failureUrl" value="${shiro.failureUrl}"/> </bean> <bean id="ShiroCasRealm" class="com.hjzgg.client.shiro.ShiroCasRealm"/> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="ShiroCasRealm"/> <property name="subjectFactory" ref="casSubjectFactory"/> </bean> <bean id="casSubjectFactory" class="org.apache.shiro.cas.CasSubjectFactory"/> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/> <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> <property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/> <property name="arguments" ref="securityManager"/> </bean> </beans>
引入shiro的需要属性
cas.loginUrl=http://127.0.0.1:8080/cas-web/login?service=http://127.0.0.1:8080/cas-client/shiro-cas
cas.logoutUrl=http://127.0.0.1:8080/cas-web/logout?service=http://127.0.0.1:8080/cas-client/shiro-cas
cas.serverUrlPrefix=http://127.0.0.1:8080/cas-web
shiro.cas.service=http://127.0.0.1:8080/cas-client/shiro-cas
shiro.failureUrl=/error
shiro.successUrl=/success
自定义shiro的realm
package com.hjzgg.client.shiro; 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.authz.SimpleAuthorizationInfo; import org.apache.shiro.cas.CasAuthenticationException; import org.apache.shiro.cas.CasToken; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.subject.SimplePrincipalCollection; import org.apache.shiro.util.StringUtils; import org.jasig.cas.client.authentication.AttributePrincipal; import org.jasig.cas.client.util.AssertionHolder; import org.jasig.cas.client.validation.Assertion; import org.jasig.cas.client.validation.Cas20ServiceTicketValidator; import org.jasig.cas.client.validation.TicketValidationException; import org.jasig.cas.client.validation.TicketValidator; import org.springframework.beans.factory.annotation.Value; import java.util.ArrayList; import java.util.List; import java.util.Map; public class ShiroCasRealm extends AuthorizingRealm { @Value("${shiro.cas.service}") private String shiroCasServiceUrl; @Value("${cas.serverUrlPrefix}") private String casServerUrlPrefix; @Override protected AuthorizationInfo doGetAuthorizationInfo( PrincipalCollection principals) { AttributePrincipal principal = AssertionHolder.getAssertion().getPrincipal(); if (principal != null) { Map<String, Object> attributes = principal.getAttributes(); if (attributes.size() > 0) { // List<String> roles = CommonUtils.arrayStringtoArrayList((String)attributes.get("roles")); List<String> roles = null; //权限信息对象info,用来存放查出的用户的所有的角色(role)及权限(permission) SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); //用户的角色集合 info.addRoles(roles); //用户的角色对应的所有权限,如果只使用角色定义访问权限,下面的一行可以不要 //info.addStringPermissions(user.getPermissionList()); } } return null; } @Override protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken token) throws AuthenticationException { CasToken casToken = (CasToken) token; if (token == null) return null; String ticket = (String) casToken.getCredentials(); if (!StringUtils.hasText(ticket)) return null; Cas20ServiceTicketValidator cas20ServiceTicketValidator = new Cas20ServiceTicketValidator(casServerUrlPrefix); cas20ServiceTicketValidator.setEncoding("utf-8"); TicketValidator ticketValidator = cas20ServiceTicketValidator; try { Assertion casAssertion = ticketValidator.validate(ticket, shiroCasServiceUrl); AttributePrincipal casPrincipal = casAssertion.getPrincipal(); String userId = casPrincipal.getName(); List principals = new ArrayList<String>(); if (casPrincipal != null) { Map<String, Object> attributes = casPrincipal.getAttributes(); principals.add(userId); principals.add(attributes); } PrincipalCollection principalCollection = new SimplePrincipalCollection(principals, getName()); return new SimpleAuthenticationInfo(principalCollection, ticket); } catch (TicketValidationException e) { throw new CasAuthenticationException((new StringBuilder()).append("Unable to validate ticket [").append(ticket).append("]").toString(), e); } } @Override protected void onInit() { super.onInit(); this.setAuthenticationTokenClass(CasToken.class); } }
引入日志系统
http://www.cnblogs.com/hujunzheng/p/6926429.html
遇到的问题
项目地址
https://github.com/hjzgg/cas4.2.7-authentication/tree/shiro+cas