1、引入需要的依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-cas</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
2、创建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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd ">
<!-- CasFilter为自定义的单点登录过滤拦截Fileter -->
<bean id="casFilter" class="org.apache.shiro.cas.CasFilter">
<!-- ${cas.server.url}是直接从yml中取的值 -->
<property name="failureUrl" value="${cas.server.url}/login?service=${cas.client.url}/shiro-cas"/>
</bean>
<!-- (非单点登录)自定义登录认证relm -->
<!-- <bean id="loginAuthCasRealm" class="com.cetc.data.frame.authority.realm.LoginAuthRealm"></bean> -->
<!-- 单点登录下的权限认证 -->
<bean id="loginAuthCasRealm" class="cn.toroot.bj.config.shiro.LoginAuthCasRealm">
<!-- cas服务端地址前缀 -->
<property name="casServerUrlPrefix" value="${cas.server.url}"></property>
<!-- 应用服务地址,用来接收cas服务端票据,客户端的cas入口 客户端的回调地址设置,必须和上面的shiro-cas过滤器casFilter拦截的地址一致 -->
<property name="casService" value="${cas.client.url}/shiro-cas"></property>
</bean>
<!-- 登出过滤器 使用了shiro session无法实现多系统单点退出-->
<bean id="logoutFilter" class="org.apache.shiro.web.filter.authc.LogoutFilter">
<!-- 配置验证错误时的失败页面 -->
<property name="redirectUrl" value="${cas.server.url}/logout?service=${cas.client.url}/shiro-cas"></property>
</bean>
<!-- cas服务器登出过滤器 拦截从cas服务器过来的各个应用客户端的logout请求,自行销毁自己的shiro session-->
<bean id="casLogoutFilter" class="cn.toroot.bj.config.shiro.CasLogoutFilter">
<property name="sessionManager" ref="sessionManager"></property>
</bean>
<!-- Shiro Filter拦截器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- securityManager -->
<property name="securityManager" ref="securityManager"></property>
<!-- 登录路径 -->
<property name="loginUrl" value="${cas.server.url}/login?service=${cas.client.url}/shiro-cas"></property>
<!-- 成功地址 -->
<property name="successUrl" value="portal/page/main.jsp"></property>
<!-- 用户访问无权限时跳转链接 -->
<property name="unauthorizedUrl" value="/portal/page/noAuthority"></property>
<!-- 过滤连定义 -->
<property name="filterChainDefinitions">
<value>
/shiro-cas=casLogout,casFilter
/loginAuth/user/loginout=logout
/query/**=anon
/authority/organize/**=anon
/authority/resource/menuscross=anon
/api/subOrg/*=anon
/api/superiorService/*=anon
/api/external/*=anon
/swagger-ui/*=anon
/**=anon <!-- 开发时放开 -->
<!--/**=casLogout,authc --> <!-- 开发时注释 -->
<!-- /loginAuth/user/login=anon -->
<!-- /portal/page/login.jsp=anon -->
<!-- /portal/script/**=anon -->
<!-- /portal/style/**=anon -->
<!-- /portal/page/main.jsp=authc -->
<!--,userPagePerm-->
</value>
</property>
<property name="filters">
<map>
<!-- 添加登出过滤 -->
<entry key="logout" value-ref="logoutFilter"></entry>
<!-- 添加casFilter到shiroFilter整合 -->
<entry key="casFilter" value-ref="casFilter"></entry>
<entry key="casLogout" value-ref="casLogoutFilter"></entry>
</map>
</property>
</bean>
<bean id="casSubjectFactory" class="org.apache.shiro.cas.CasSubjectFactory"></bean>
<!-- securityManager -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="loginAuthCasRealm"/>
<property name="sessionManager" ref="sessionManager"></property>
<property name="cacheManager" ref="cacheManager"></property>
<property name="subjectFactory" ref="casSubjectFactory"></property>
</bean>
<!-- shiro的默认缓存(本地缓存) -->
<bean id="cacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager"></bean>
<!--保证实现了Shiro内部lifecycle函数的bean执行 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"></bean>
<!-- Shiro会话管理器 -->
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<!-- Shiro会话30分钟失效 -->
<property name="globalSessionTimeout" value="1800000"></property>
<property name="deleteInvalidSessions" value="true"></property>
<property name="sessionValidationSchedulerEnabled" value="true"></property>
</bean>
<!-- 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证 -->
<!-- 当使用注解时如果没有该权限或角色的处理 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor">
<property name="proxyTargetClass" value="true"></property>
</bean>
<!-- 用于开启 Shiro Spring AOP 权限注解的支持 -->
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager" />
</bean>
</beans>
3、创建权限认证类继承CasRealm
package cn.toroot.bj.config.shiro;
import cn.toroot.bj.config.shiro.service.BaseService;
import cn.toroot.bj.config.shiro.entity.Resource;
import cn.toroot.bj.config.shiro.entity.Role;
import org.springframework.util.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cas.CasRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
import java.util.Map;
@SuppressWarnings("deprecation")
public class LoginAuthCasRealm extends CasRealm {
@Autowired
private BaseService baseService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals){
String username=(String) principals.fromRealm(getName()).iterator().next();
if(!StringUtils.isEmpty(username)){
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
List<Role> roles = baseService.getRoleByUserName(username);
if(roles!=null&&roles.size()>0){
for (Role role : roles) {
info.addRole(role.getRoleName());
}
}
List<Resource> resources = baseService.getResourceByUserName(username);
if(resources!=null&&resources.size()>0){
for (Resource resource : resources) {
info.addStringPermission(resource.getFullName());
}
}
System.out.println("当前用户拥有角色和权限分别为:");
System.out.println(info.getRoles());
System.out.println(info.getStringPermissions());
return info;
}
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token) throws AuthenticationException {
AuthenticationInfo authc = super.doGetAuthenticationInfo(token);
String name = (String) authc.getPrincipals().getPrimaryPrincipal();
Map<String, Object> userInfo = baseService.getUserInfo(name);
SecurityUtils.getSubject().getSession().setAttribute("userInfo", userInfo);
System.out.println("登录SESSIONID:"+SecurityUtils.getSubject().getSession().getId());
return authc;
}
}
4、直接上其他相关配置,这里就不一一介绍了
package cn.toroot.bj.config.shiro;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.servlet.AdviceFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
public class CasLogoutFilter extends AdviceFilter{
private static final Logger log = LoggerFactory.getLogger(CasLogoutFilter.class);
private static final SingleSignOutHandler HANDLER = new SingleSignOutHandler();
private SessionManager sessionManager;
public void setSessionManager(SessionManager sessionManager) {
this.sessionManager = sessionManager;
}
@Override
protected boolean preHandle(ServletRequest request, ServletResponse response)
throws Exception {
HttpServletRequest req = (HttpServletRequest) request;
if(HANDLER.isTokenRequest(req)){
HANDLER.recordSession(req);
}else if (HANDLER.isLogoutRequest(req)) {
HANDLER.invalidateSession(req, sessionManager);
return false;
}else {
log.trace("Ignoring URI "+req.getRequestURI());
}
Subject subject = SecurityUtils.getSubject();
Session session = subject.getSession(false);
if(session!=null&&session.getAttribute(HANDLER.getLogoutParameterName())!=null){
try {
subject.logout();
} catch (Exception e) {
log.debug("Encountered session exception during logout. This can generally safely be ignored.",e);
}
}
return true;
}
}
package cn.toroot.bj.config.shiro;
import org.apache.shiro.cas.CasFilter;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.jasig.cas.client.session.SingleSignOutFilter;
import org.jasig.cas.client.session.SingleSignOutHttpSessionListener;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;
@Configuration
public class FilterConfig2 {
public FilterConfig2() {
}
@Bean
public FilterRegistrationBean delegatingFilterProxy() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new DelegatingFilterProxy("shiroFilter"));
filterRegistrationBean.addInitParameter("targetFilterLifecycle", "true");
filterRegistrationBean.setEnabled(true);
filterRegistrationBean.addUrlPatterns(new String[]{"/*"});
return filterRegistrationBean;
}
@Bean
public ServletListenerRegistrationBean singleSignOutHttpSessionListener() {
ServletListenerRegistrationBean bean = new ServletListenerRegistrationBean();
bean.setListener(new SingleSignOutHttpSessionListener());
bean.setEnabled(true);
return bean;
}
@Bean
public FilterRegistrationBean singleSignOutFilter() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setName("singleSignOutFilter");
filterRegistrationBean.setFilter(new SingleSignOutFilter());
filterRegistrationBean.addUrlPatterns(new String[]{"/*"});
filterRegistrationBean.setEnabled(true);
return filterRegistrationBean;
}
@Bean
public FilterRegistrationBean registLogoutFilter(LogoutFilter logoutFilter) {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(logoutFilter, new ServletRegistrationBean[0]);
filterRegistrationBean.setEnabled(false);
return filterRegistrationBean;
}
@Bean
public FilterRegistrationBean registCasFilter(CasFilter casFilter) {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(casFilter, new ServletRegistrationBean[0]);
filterRegistrationBean.setEnabled(false);
return filterRegistrationBean;
}
@Bean
public FilterRegistrationBean registCasLogoutFilter(CasLogoutFilter casLogoutFilter) {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(casLogoutFilter, new ServletRegistrationBean[0]);
filterRegistrationBean.setEnabled(false);
return filterRegistrationBean;
}
}
package cn.toroot.bj.config.shiro;
import org.apache.shiro.session.Session;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
public final class HashMapBackedSessionMappingStorage {
private final Map<String,Serializable> MANAGED_SESSIONS_ID = new HashMap<>();
public synchronized void addSessionById(String mappingId,Session session){
MANAGED_SESSIONS_ID.put(mappingId, session.getId());
}
public synchronized Serializable getSessionIDByMappingId(String mappingId){
return MANAGED_SESSIONS_ID.get(mappingId);
}
}
package cn.toroot.bj.config.shiro;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.DefaultSessionKey;
import org.apache.shiro.session.mgt.SessionManager;
import org.jasig.cas.client.util.CommonUtils;
import org.jasig.cas.client.util.XmlUtils;
import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
public class SingleSignOutHandler {
private final Log log = LogFactory.getLog(getClass());
private String artifactParameterName = "ticket";
private String logoutParameterName = "logoutRequest";
private HashMapBackedSessionMappingStorage storage = new HashMapBackedSessionMappingStorage();
protected SingleSignOutHandler(){
init();
}
public void setArtifactParameterName(final String name) {
this.artifactParameterName = name;
}
public String getLogoutParameterName() {
return logoutParameterName;
}
public void setLogoutParameterName(final String name) {
this.logoutParameterName = name;
}
public void init(){
CommonUtils.assertNotNull(this.artifactParameterName,"artifactParameterName cannot be null.");
CommonUtils.assertNotNull(this.logoutParameterName,"logoutParameterName cannot be null.");
}
public boolean isTokenRequest(final HttpServletRequest request){
return CommonUtils.isNotBlank(CommonUtils.safeGetParameter(request, this.artifactParameterName));
}
public boolean isLogoutRequest(final HttpServletRequest request){
return "POST".equals(request.getMethod()) && !isMultipartRequest(request) &&
CommonUtils.isNotBlank(CommonUtils.safeGetParameter(request, this.logoutParameterName));
}
public void recordSession(final HttpServletRequest request){
Session session = SecurityUtils.getSubject().getSession();
final String token = CommonUtils.safeGetParameter(request, this.artifactParameterName);
if(log.isDebugEnabled()){
log.debug("Recording session for token" + token);
}
storage.addSessionById(token, session);
}
public void invalidateSession(final HttpServletRequest request,final SessionManager sessionManager){
final String logoutMessage = CommonUtils.safeGetParameter(request, this.logoutParameterName);
if(log.isTraceEnabled()){
log.trace("Logout request:\n" + logoutMessage);
}
final String token = XmlUtils.getTextForElement(logoutMessage, "SessionIndex");
if(CommonUtils.isNotBlank(token)){
Serializable sessionId = storage.getSessionIDByMappingId(token);
if(sessionId != null){
try {
Session session = sessionManager.getSession(new DefaultSessionKey(sessionId));
if(session != null){
session.setAttribute(logoutParameterName, true);
if(log.isDebugEnabled()){
log.debug("Invalidating session["+sessionId+"] for token ["+token +"]");
}
}
} catch (Exception e) {
}
}
}
}
private boolean isMultipartRequest(final HttpServletRequest request){
return request.getContentType() != null && request.getContentType().toLowerCase().startsWith("multipart");
}
}
5、上yml配置内容,最后springBoot启动类读取cas_shiro.xml即可
cas:
server:
url: http://192.168.91.103:8083/sso-cas
client:
url: http://28.9.150.68:8081/das