本篇我们讲解cas单点登陆在与shiro集成,在与redis集成遇到的问题
先看完整代码吧
package com.madnet.config;
import com.google.common.base.CaseFormat;
import com.madnet.module.permission.repository.PermissionRepository;
import com.madnet.module.permission.service.PermissionService;
import io.buji.pac4j.filter.CallbackFilter;
import io.buji.pac4j.filter.LogoutFilter;
import io.buji.pac4j.filter.SecurityFilter;
import io.buji.pac4j.realm.Pac4jRealm;
import io.buji.pac4j.subject.Pac4jSubjectFactory;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.spring.web.config.AbstractShiroWebFilterConfiguration;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.tomcat.util.descriptor.web.FilterMap;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.jasig.cas.client.authentication.AuthenticationFilter;
import org.pac4j.cas.client.CasClient;
import org.pac4j.cas.client.rest.CasRestFormClient;
import org.pac4j.cas.config.CasConfiguration;
import org.pac4j.cas.config.CasProtocol;
import org.pac4j.core.client.Clients;
import org.pac4j.core.config.Config;
import org.pac4j.core.matching.PathMatcher;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.web.filter.DelegatingFilterProxy;
import javax.servlet.Filter;
import javax.servlet.FilterConfig;
import java.awt.*;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.shiro.cas.CasFilter;
import org.crazycake.shiro.RedisCacheManager;
/**
* 对shiro的安全配置,是对cas的登录策略进行配置
*
* @author Carl
* @date 2017/9/16
* @since 1.0.0
*/
@Configuration
public class ShiroConfiguration extends AbstractShiroWebFilterConfiguration {
@Value("#{ @environment['cas.prefixUrl'] ?: null }")
private String prefixUrl;
@Value("#{ @environment['cas.loginUrl'] ?: null }")
private String casLoginUrl;
@Value("#{ @environment['cas.callbackUrl'] ?: null }")
private String callbackUrl;
@Value("#{ @environment['cas.serviceUrl'] ?: null }")
private String serviceUrl;
@Autowired
private PermissionService permissionService;
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private int port;
@Value("${spring.redis.timeout}")
private String timeout;
/**
* cas核心过滤器,把支持的client写上,filter过滤时才会处理,clients必须在casConfig.clients已经注册
*
* @return
*/
@Bean
public Filter casSecurityFilter() {
SecurityFilter filter = new SecurityFilter();
filter.setClients("cas,rest");
filter.setConfig(casConfig());
return filter;
}
/**
* 通过rest接口可以获取tgt,获取service ticket,甚至可以获取CasProfile
*
* @return
*/
@Bean
protected CasRestFormClient casRestFormClient() {
CasRestFormClient casRestFormClient = new CasRestFormClient();
casRestFormClient.setConfiguration(casConfiguration());
casRestFormClient.setName("rest");
return casRestFormClient;
}
@Bean
public PathMatcher pathMatcher() {
PathMatcher pathMatcher = new PathMatcher();
pathMatcher.excludePath("/**");
return pathMatcher;
}
@Bean
protected Clients clients() {
//可以设置默认client
Clients clients = new Clients();
//支持的client全部设置进去
clients.setClients(casClient(), casRestFormClient());
return clients;
}
@Bean
public CasClient casClient() {
CasClient casClient = new CasClient();
casClient.setConfiguration(casConfiguration());
casClient.setCallbackUrl(callbackUrl);
casClient.setName("cas");
return casClient;
}
@Bean
protected Config casConfig() {
Config config = new Config();
config.setClients(clients());
return config;
}
/**
* cas的基本设置,包括或url等等,rest调用协议等
*
* @return
*/
@Bean
public CasConfiguration casConfiguration() {
CasConfiguration casConfiguration = new CasConfiguration(casLoginUrl);
casConfiguration.setProtocol(CasProtocol.CAS30);
casConfiguration.setPrefixUrl(prefixUrl);
return casConfiguration;
}
/**
* 路径过滤设置
*
* @return
*/
@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition() {
DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();
definition.addPathDefinition("/login2", "casSecurityFilter");
definition.addPathDefinition("/login.jsp", "casSecurityFilter");
definition.addPathDefinition("/callback", "callbackFilter");
definition.addPathDefinition("/logout", "logoutFilter");
return definition;
}
@Bean
protected Map<String, Filter> shiroFilters() {
//过滤器设置
Map<String, Filter> filters = new HashMap<>();
filters.put("casSecurityFilter", casSecurityFilter());
CallbackFilter callbackFilter = new CallbackFilter();
callbackFilter.setConfig(casConfig());
filters.put("callbackFilter", callbackFilter);
LogoutFilter logoutFilter = new LogoutFilter();
logoutFilter.setConfig(casConfig());
logoutFilter.setCentralLogout(true);
logoutFilter.setDefaultUrl(serviceUrl);
filters.put("logoutFilter", logoutFilter);
return filters;
}
@Bean
public Realm pac4jRealm() {
return new RealmConfiguration();
}
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setSubjectFactory(new Pac4jSubjectFactory());
securityManager.setRealm(pac4jRealm());
// securityManager.setSessionManager(SessionManager());
// securityManager.setCacheManager(cacheManager());
return securityManager;
}
@Bean(name = "casFilter")
public CasFilter getCasFilter() {
CasFilter casFilter = new CasFilter();
casFilter.setName("casFilter");
casFilter.setEnabled(true);
casFilter.setLoginUrl("/login2");
// 登录失败后跳转的URL,也就是 Shiro 执行 CasRealm 的 doGetAuthenticationInfo 方法向CasServer验证tiket
casFilter.setFailureUrl(loginUrl);// 我们选择认证失败后再打开登录页面
return casFilter;
}
/**
* 对过滤器进行调整
*
* @return
*/
@Bean("shiroFilter")
protected ShiroFilterFactoryBean shiroFilterFactoryBean() {
((DefaultSecurityManager) securityManager).setSubjectFactory(new Pac4jSubjectFactory());
ShiroFilterFactoryBean filterFactoryBean = super.shiroFilterFactoryBean();
filterFactoryBean.setSecurityManager(securityManager);
filterFactoryBean.setFilters(shiroFilters());
Map<String, String> initParameters = new HashMap();
initParameters.put("/callback","callbackFilter");
initParameters.put("/logout","logoutFilter");
// initParameters.put("/upload.html", "casSecurityFilter");
initParameters.put("/static/**", "anon");
initParameters.put("/upload.html", "authc");
initParameters.putAll(permissionService.getAuthcMap());
filterFactoryBean.setFilterChainDefinitionMap(initParameters);
filterFactoryBean.setLoginUrl("/login2");
// 登录成功后要跳转的链接
filterFactoryBean.setSuccessUrl("/index.html");
// 未授权界面;
filterFactoryBean.setUnauthorizedUrl("/login2");
return filterFactoryBean;
}
/**
* 对shiro的过滤策略进行明确
*
* @return
*/
@Bean
public FilterRegistrationBean delegatingFilterProxy() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
DelegatingFilterProxy proxy = new DelegatingFilterProxy();
proxy.setTargetFilterLifecycle(true);
proxy.setTargetBeanName("shiroFilter");
filterRegistrationBean.setFilter(proxy);
return filterRegistrationBean;
}
/**
* 开启 shiro aop注解支持
*
* @param securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor();
aasa.setSecurityManager(securityManager);
return aasa;
}
@Bean
public FilterRegistrationBean someFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(casSecurityFilter());
registration.addUrlPatterns("/login2");
registration.setName("casSecurityFilter");
return registration;
}
@Bean
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}
/**
* 配置shiro redisManager
*
* @return
*/
@Bean
public RedisManager redisManager() {
RedisManager redisManager = new RedisManager();
redisManager.setHost(host);
redisManager.setPort(port);
redisManager.setExpire(18000);// 配置过期时间
//redisManager.setTimeout(Integer.parseInt(timeout));
// redisManager.setPassword(String.valueOf(password));
return redisManager;
}
/**
* cacheManager 缓存 redis实现
*
* @return
*/
@Bean(name="cach")
public RedisCacheManager cacheManager() {
RedisCacheManager redisCacheManager = new RedisCacheManager();
redisCacheManager.setRedisManager(redisManager());
return redisCacheManager;
}
/**
* RedisSessionDAO shiro sessionDao层的实现 通过redis
*/
@Bean
public RedisSessionDAO redisSessionDAO() {
RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
redisSessionDAO.setRedisManager(redisManager());
return redisSessionDAO;
}
/**
* shiro session的管理
*/
@Bean
public DefaultWebSessionManager SessionManager() {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
//sessionManager.setSessionIdCookie(sessionMeCookie());
sessionManager.setSessionDAO(redisSessionDAO());
return sessionManager;
}
@Bean
public SimpleCookie sessionMeCookie() {
SimpleCookie simpleCookie = new SimpleCookie("madnet-s-org");
simpleCookie.setMaxAge(-1);
simpleCookie.setDomain(".mad-net.org");
return simpleCookie;
}
@Bean
public SimpleCookie rememberMeCookie() {
// 这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
SimpleCookie simpleCookie = new SimpleCookie("madnet-re-org");
simpleCookie.setMaxAge(18000);
// <!-- 记住我cookie生效时间30天 ,单位秒;-->
// simpleCookie.setMaxAge(2592000);
simpleCookie.setDomain(".mad-net.org");
return simpleCookie;
}
}
cas:
prefixUrl: https://172.17.1.10:8443/cas
loginUrl: ${cas.prefixUrl}/login
serviceUrl: http://172.17.1.10:${server.port}
callbackUrl: ${cas.serviceUrl}/callback
jwt:
salt: 12345678901234567890123456789012
debug: true
若想同时使用
setFilterChainDefinitionMap 和
setFilters
你一定要把规则写到setFilterChainDefinitionMap这个方法中,具体方法看代码吧