title: shiro-cas 单点登录爬过的坑
notebook: 笔记
tags:java
主要来讲shiro-cas +spring的配置
首先shiro的简单配置就不多说了 总结下步骤:
- 配置web.xml使用shiroFilter
<!-- 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>
2.使用spring配置shiro
<!-- 用于认证的realm -->
<bean class="com.hahaee.shiro.realm.AuthRealm" id="authRealm"/>
<!-- 注意这里shiroFilter 要与web.xml中的filter-name一致-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- shiro的核心安全接口 -->
<property name="securityManager" ref="securityManager"/>
<!-- 要求登录时的链接 -->
<!--<property name="loginUrl" value="/login.jsp"/>-->
<!-- 登陆成功后要跳转的连接 -->
<property name="successUrl" value="/success.jsp"/>
<!-- 没有权限要跳转的链接 -->
<property name="unauthorizedUrl" value="/unauthro.jsp"/>
<property name="filters">
<util:map>
</util:map>
</property>
<!-- shiro连接约束配置-->
<property name="filterChainDefinitions">
<value>
<!-- url匹配权限控制 -->
/login/*=anon
/** = authc
</value>
</property>
</bean>
<!-- 安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realms">
<list>
<!--配置realm-->
<ref bean="authRealm"/>
</list>
</property>
</bean>
<!-- Shiro生命周期处理器 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<!-- 相当于调用SecurityUtils.setSecurityManager(securityManager) -->
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/>
<property name="arguments" ref="securityManager"/>
</bean>
- 方法中调用subject.login(token)来通过shiro进行认证
SecurityUtils.setSecurityManager(securityManager);
Subject currentUser = SecurityUtils.getSubject();
if (!currentUser.isAuthenticated()) {
UsernamePasswordToken token = new UsernamePasswordToken(username, pwd);
token.setRememberMe(true);
try {
currentUser.login(token);
} catch (Exception e) {
e.printStackTrace();
}
}
- 继承AuthenticatingRealm编写认证流程
public class AuthRealm extends AuthenticatingRealm {
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authToken) throws AuthenticationException {
String username = (String) authToken.getPrincipal();
if (username.equals("admin")) {
return new SimpleAuthenticationInfo(
"admin", "admin", //加密密码
getName() //realm name
);
} else {
return null;
}
}
}
shiro的登入认证就到此为止 关于权限认证不在本文范畴
shiro-cas +spring 配置
说下cas的结构:
1. cas分为server 和client端
2. cas-client通过filter来拦截用户的url请求 主要有AuthenticationFilter和Cas20ProxyReceivingTicketValidationFilter 前者负责登录验证 后者负责ticket的有效验证
3. 当通过server登录后 server会发送http请求到client 并传递ticket的参数
4. client通过检测ticket的存在来判断是否登录过 检测ticket的有效(过期与否)来判断登录状态是否有效
5. 当通过server登出后 同样会发送http请求到client端 并传递登出参数 client通过SingleSignOutFilter拦截请求并清除本地登录状态 实现登出
shiro如何通过cas来进行登录:
1. shiro本身通过继承realm的方式来进行登录认证 当接入cas后 则通过cas-server来进行登录认证 详见org.apache.shiro.cas.CasRealm
2. 通过shiroFilter来对请求路径的登录状态验证 详见org.apache.shiro.cas.CasFilter
配置过程:
- 配置web.xml使用shiroFilter
<!-- 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>
2.spring配置shiro
<!-- Shiro Filter -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="loginUrl" value="${shiro.loginUrl}"/>
<property name="securityManager" ref="securityManager"/>
<!-- 设定用户的登录链接,这里为cas登录页面的链接地址可配置回调地址 -->
<property name="filters">
<map>
<!-- 添加casFilter到shiroFilter -->
<entry key="logoutFilter" value-ref="logoutFilter"/>
<entry key="casFilter" value-ref="casFilter"/>
</map>
</property>
<property name="filterChainDefinitions">
<value>
/logout= logoutFilter
/login = casFilter
/protected/** = roles[ROLE_USER]
/** = anon
</value>
</property>
</bean>
<bean id="casFilter" class="org.apache.shiro.cas.CasFilter">
<!-- 配置验证错误时的失败页面 -->
<property name="failureUrl" value="${shiro.failureUrl}"/>
<property name="successUrl" value="${shiro.successUrl}"/>
</bean>
<bean id="logoutFilter" class="org.apache.shiro.web.filter.authc.LogoutFilter">
<!-- 配置验证错误时的失败页面 -->
<property name="redirectUrl" value="${shiro.logoutUrl}"/>
</bean>
<bean id="casRealm" class="org.apache.shiro.cas.CasRealm">
<property name="defaultRoles" value="ROLE_USER"/>
<!-- cas服务端地址前缀 -->
<property name="casServerUrlPrefix" value="${shiro.cas.serverUrlPrefix}"/>
<!-- 应用服务地址,用来接收cas服务端票据 -->
<property name="casService" value="${shiro.cas.service}"/>
</bean>
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="subjectFactory" ref="casSubjectFactory"/>
<property name="realm" ref="casRealm"/>
</bean>
<bean id="casSubjectFactory" class="org.apache.shiro.cas.CasSubjectFactory"/>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
<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>
<!-- properties -->
shiro.loginUrl=http://server:8080/login?service=http://client2:8082/login
shiro.logoutUrl=http://server:8080/logout?service=http://client2:8082/login
shiro.cas.serverUrlPrefix=http://server:8080/
shiro.cas.service=http://client2:8082/login
shiro.failureUrl=/fail.jsp
shiro.successUrl=/success.jsp
配置完成 说明一下注意的地方:
logoutFilter 和casFilter 分别作用是清除登录状态 和保存登录状态 而不是来判断是否有登录状态的!
shiro.failureUrl=/fail.jsp 和 shiro.successUrl=/success.jsp 是访问/login时 是否登录时跳转的页面
/logout = logoutFilter /logout是个虚拟的路径 作用是当访问/logout时 调起logoutFilter来完成登出的操作(注意只是清理了本地的登录状态 要想清楚cas-server的登录状态 则要通过redirectUrl来访问cas-server的登出接口)
/login = casFilter /login也是个虚拟路径 作用是 当通过server登录成功后 回跳到/login时会被casFilter拦截 从而保存下登录信息
如果通过cas-server登出 而不是通过logoutFilter登出 那么client还是持有登录状态 因为client只通过cas进行一次登录认证 其他都是直接判断本地保存的登录状态 来判断登录与否的
解决方式:
<!-- 通过filter来拦截server登出时发出的client请求 来手动重置本地保存的登录状态 -->
<filter>
<filter-name>logoutFilter</filter-name>
<filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>logoutFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- CAS: 用于单点退出 -->
<listener>
<listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>
</listener>
以上 爬了一天的坑的总结