package com.zhibi.xiuba.mgr.conf.shiro;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authc.pam.AuthenticationStrategy;
import org.apache.shiro.authc.pam.FirstSuccessfulStrategy;
import org.apache.shiro.authz.ModularRealmAuthorizer;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.servlet.ShiroHttpSession;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import javax.servlet.Filter;
import java.util.*;
/**
* Shiro 配置
* Apache Shiro 核心通过 Filter 来实现,就好像SpringMvc 通过DispachServlet 来主控制一样。
* 既然是使用 Filter 一般也就能猜到,是通过URL规则来进行过滤和权限校验,所以我们需要定义一系列关于URL的规则和访问权限。
*/
@Configuration
public class ShiroConfiguration {
private Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 凭证匹配器
* (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了
* 所以我们需要修改下doGetAuthenticationInfo中的代码;
* )
*
* @return
*/
@Bean(name = "adminHashedCredentialsMatcher")
public HashedCredentialsMatcher adminHashedCredentialsMatcher() {
logger.info("ShiroConfiguration.adminHashedCredentialsMatcher()");
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("md5");
hashedCredentialsMatcher.setHashIterations(2);
return hashedCredentialsMatcher;
}
@Bean(name = "customHashedCredentialsMatcher")
public HashedCredentialsMatcher customHashedCredentialsMatcher() {
logger.info("ShiroConfiguration.adminHashedCredentialsMatcher()");
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("md5");
hashedCredentialsMatcher.setHashIterations(1);
return hashedCredentialsMatcher;
}
/**
* 前台身份认证realm;
*
* @return
*/
@Bean(name = "customShiroRealm")
public CustomShiroRealm customShiroRealm() {
logger.info("ShiroConfiguration.customShiroRealm()");
CustomShiroRealm customShiroRealm = new CustomShiroRealm();
customShiroRealm.setCredentialsMatcher(customHashedCredentialsMatcher());
return customShiroRealm;
}
/**
* 后台身份认证realm;
*
* @return
*/
@Bean(name = "adminShiroRealm")
public AdminShiroRealm adminShiroRealm() {
logger.info("ShiroConfiguration.adminShiroRealm()");
AdminShiroRealm adminShiroRealm = new AdminShiroRealm();
adminShiroRealm.setCredentialsMatcher(adminHashedCredentialsMatcher());
return adminShiroRealm;
}
/**
* shiro缓存管理器;
* 需要注入对应的其它的实体类中:
* 1、安全管理器:securityManager
* 可见securityManager是整个shiro的核心;
*
* @return
*/
@Bean(name = "ehCacheManager")
public EhCacheManager ehCacheManager() {
logger.info("ShiroConfiguration.ehCacheManager()");
EhCacheManager cacheManager = new EhCacheManager();
cacheManager.setCacheManagerConfigFile("classpath:ehcache-shiro.xml");
return cacheManager;
}
/**
* Shiro默认提供了三种 AuthenticationStrategy 实现:
* AtLeastOneSuccessfulStrategy :其中一个通过则成功。
* FirstSuccessfulStrategy :其中一个通过则成功,但只返回第一个通过的Realm提供的验证信息。
* AllSuccessfulStrategy :凡是配置到应用中的Realm都必须全部通过。
* authenticationStrategy
*
* @return
*/
@Bean(name = "authenticationStrategy")
public AuthenticationStrategy authenticationStrategy() {
logger.info("ShiroConfiguration.authenticationStrategy()");
return new FirstSuccessfulStrategy();
}
/**
* @return
* @see DefaultWebSessionManager
*/
@Bean(name = "sessionManager")
public DefaultWebSessionManager defaultWebSessionManager() {
logger.info("ShiroConfiguration.defaultWebSessionManager()");
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setSessionDAO(redisSessionDAO());
sessionManager.setCacheManager(ehCacheManager());
sessionManager.setSessionValidationInterval(3600000 * 12);
sessionManager.setGlobalSessionTimeout(3600000 * 12);
sessionManager.setDeleteInvalidSessions(true);
sessionManager.setSessionValidationSchedulerEnabled(true);
Cookie cookie = new SimpleCookie(ShiroHttpSession.DEFAULT_SESSION_ID_NAME);
cookie.setName("WEBID");
cookie.setHttpOnly(true);
sessionManager.setSessionIdCookie(cookie);
return sessionManager;
}
@Bean
public SessionDAO redisSessionDAO() {
RedisSessionDAO sessionDAO = new RedisSessionDAO();
return sessionDAO;
}
/**
* @return
* @see org.apache.shiro.mgt.SecurityManager
*/
@Bean(name = "securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManage() {
logger.info("ShiroConfiguration.getDefaultWebSecurityManage()");
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
Map<String, Object> shiroAuthenticatorRealms = new HashMap<>();
shiroAuthenticatorRealms.put("adminShiroRealm", adminShiroRealm());
shiroAuthenticatorRealms.put("customShiroRealm", customShiroRealm());
Collection<Realm> shiroAuthorizerRealms = new ArrayList<Realm>();
shiroAuthorizerRealms.add(adminShiroRealm());
shiroAuthorizerRealms.add(customShiroRealm());
CustomModularRealmAuthenticator customModularRealmAuthenticator = new CustomModularRealmAuthenticator();
customModularRealmAuthenticator.setDefinedRealms(shiroAuthenticatorRealms);
customModularRealmAuthenticator.setAuthenticationStrategy(authenticationStrategy());
securityManager.setAuthenticator(customModularRealmAuthenticator);
ModularRealmAuthorizer customModularRealmAuthorizer = new ModularRealmAuthorizer();
customModularRealmAuthorizer.setRealms(shiroAuthorizerRealms);
securityManager.setAuthorizer(customModularRealmAuthorizer);
securityManager.setCacheManager(ehCacheManager());
securityManager.setSessionManager(defaultWebSessionManager());
return securityManager;
}
/**
* 开启shiro aop注解支持.
* 使用代理方式;所以需要开启代码支持;
*
* @param securityManager
* @return
*/
@Bean(name = "authorizationAttributeSourceAdvisor")
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
logger.info("ShiroConfiguration.authorizationAttributeSourceAdvisor()");
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
/**
* ShiroFilterFactoryBean 处理拦截资源文件问题。
* 注意:单独一个ShiroFilterFactoryBean配置是或报错的,以为在
* 初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager
* Filter Chain定义说明
* 1、一个URL可以配置多个Filter,使用逗号分隔
* 2、当设置多个过滤器时,全部验证通过,才视为通过
* 3、部分过滤器可指定参数,如perms,roles
*/
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {
logger.info("ShiroConfiguration.shiroFilter()");
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String, Filter> filters = new HashMap<>();
filters.put("admin", new AdminFormAuthenticationFilter());
filters.put("custom", new CustomFormAuthenticationFilter());
shiroFilterFactoryBean.setFilters(filters);
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
/**
* anon(匿名)org.apache.shiro.web.filter.authc.AnonymousFilter
* authc(身份验证)org.apache.shiro.web.filter.authc.FormAuthenticationFilter
* authcBasic(http基本验证)org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
* logout(退出)org.apache.shiro.web.filter.authc.LogoutFilter
* noSessionCreation(不创建session) org.apache.shiro.web.filter.session.NoSessionCreationFilter
* perms(许可验证) org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
* port(端口验证)org.apache.shiro.web.filter.authz.PortFilter
* rest (rest方面) org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
* roles(权限验证)org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
* ssl (ssl方面)org.apache.shiro.web.filter.authz.SslFilter
* member (用户方面)org.apache.shiro.web.filter.authc.UserFilter
* user表示用户不一定已通过认证,只要曾被Shiro记住过登录状态的用户就可以正常发起请求,比如rememberMe
*/
filterChainDefinitionMap.put("/**/login", "anon");
filterChainDefinitionMap.put("/console/dashboard/zhu/**", "anon");
filterChainDefinitionMap.put("/console/dashboard/phone/**", "anon");
filterChainDefinitionMap.put("/**/logout", "logout");
filterChainDefinitionMap.put("/member/reg", "anon");
filterChainDefinitionMap.put("/console/**", "admin");
filterChainDefinitionMap.put("/member/**", "custom");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
/**
* 注入LifecycleBeanPostProcessor
*
* @return
*/
@Bean(name = "lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
logger.info("ShiroConfiguration.lifecycleBeanPostProcessor()");
return new LifecycleBeanPostProcessor();
}
@ConditionalOnMissingBean
@Bean(name = "defaultAdvisorAutoProxyCreator")
@DependsOn("lifecycleBeanPostProcessor")
public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
logger.info("ShiroConfiguration.getDefaultAdvisorAutoProxyCreator()");
DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator();
daap.setProxyTargetClass(true);
return daap;
}
}
- 2、假设现在有这样一种需求:存在两张表user和admin,分别记录普通用户和管理员的信息。并且现在要实现普通用户和管理员的分开登录,即需要两个Realm——UserRealm和AdminRealm,分别处理普通用户和管理员的验证功能。
但是正常情况下,当定义了两个Realm,无论是普通用户登录,还是管理员登录,都会由这两个Realm共同处理。这是因为,当配置了多个Realm时,我们通常使用的认证器是shiro自带的org.apache.shiro.authc.pam.ModularRealmAuthenticator,
package com.zhibi.xiuba.mgr.conf.shiro;
import org.apache.shiro.ShiroException;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.util.CollectionUtils;
import java.util.Collection;
import java.util.Map;
public class CustomModularRealmAuthenticator extends ModularRealmAuthenticator {
private Map<String, Object> definedRealms;
/**
* 多个realm实现
*/
@Override
protected AuthenticationInfo doMultiRealmAuthentication(Collection<Realm> realms, AuthenticationToken token) {
return super.doMultiRealmAuthentication(realms, token);
}
/**
* 调用单个realm执行操作
*/
@Override
protected AuthenticationInfo doSingleRealmAuthentication(Realm realm,AuthenticationToken token) {
if (!realm.supports(token)) {
throw new ShiroException("token错误!");
}
AuthenticationInfo info = null;
try {
info = realm.getAuthenticationInfo(token);
if (info == null) {
throw new ShiroException("token不存在!");
}
} catch (Exception e) {
throw new ShiroException("用户名或者密码错误!");
}
return info;
}
/**
* 判断登录类型执行操作
*/
@Override
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken)throws AuthenticationException {
this.assertRealmsConfigured();
Realm realm = null;
CustomerAuthenticationToken token = (CustomerAuthenticationToken) authenticationToken;
if ("1".equals(token.getLoginType())) {
realm = (Realm) this.definedRealms.get("customShiroRealm");
}else{
realm = (Realm) this.definedRealms.get("adminShiroRealm");
}
return this.doSingleRealmAuthentication(realm, authenticationToken);
}
/**
* 判断realm是否为空
*/
@Override
protected void assertRealmsConfigured() throws IllegalStateException {
this.definedRealms = this.getDefinedRealms();
if (CollectionUtils.isEmpty(this.definedRealms)) {
throw new ShiroException("值传递错误!");
}
}
public Map<String, Object> getDefinedRealms() {
return this.definedRealms;
}
public void setDefinedRealms(Map<String, Object> definedRealms) {
this.definedRealms = definedRealms;
}
}