一:介绍
Apache Shiro是Java的一个安全框架。由于它相对小而简单,现在使用的人越来越多。
Authentication:身份认证/登录,验证用户是不是拥有相应的身份。
Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限。
二:配置
springboot application-dev.yml 配置文件
spring:
shiro:
properties:
authz: false
session-id-cookie:
http-only: false
name: yui2-token
maxAge: 2592000
session-dao:
#expire: 86400 选用globalSessionTimeout这个值
session-prefix: yui2-sid
session-mgr:
globalSessionTimeout: 86400000
shiro-db-realm:
authentication-caching-enabled: false
authorization-caching-enabled: false
authentication-cache-name: yui2-cache-authc
authorization-cache-name: yui2-cache-authz
authz:false,表示是否开启后端授权
session-id-cookie.http-only:false,如果为true,浏览器读取不到这个token。
session-id-cookie.name:yui2-token,这个值cookie的名称。
session-id-cookie.maxAge:2592000,这个cookie在浏览器保留时间。
session-dao.session-prefix: sessionId,前缀。
session-mgr.globalSessionTimeout:session过期时间。
shiro-db-realm.authentication-caching-enabled: false。表示是否开启用户信息缓存
shiro-db-realm.authorization-caching-enabled: false。 表示是否开启权限信息缓存
shiro-db-realm.authentication-cache-name: yui2-cache-authc。 表示用户信息缓存名称
shiro-db-realm. authorization-cache-name: yui2-cache-authz。 表示权限信息缓存名称
springboot配置文件,ShiroConfig.java
@Configuration
public class ShiroConfig {
private static final Logger logger = LoggerFactory.getLogger(ShiroConfig.class);
@Bean("ShiroProperties")
@ConfigurationProperties("spring.shiro.properties")
public ShiroProperties shiroProperties(){
return new ShiroProperties();
}
@Bean("shiroFilter")
//@ConfigurationProperties("spring.shiro.shiro-filter")
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager, ShiroProperties shiroProperties) throws IOException {
logger.info("ShiroConfiguration.shirFilter()");
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
//shiroFilterFactoryBean.setLoginUrl("/view/login.html");
//shiroFilterFactoryBean.setSuccessUrl("/view/index.html");
//shiroFilterFactoryBean.setUnauthorizedUrl("/view/error/403.html");
//定义过滤器
Map<String, Filter> filters = new LinkedHashMap<String, Filter>();
filters.put("fLogin", new ForceLogoutFilter());
filters.put("mauthc", new WebAndAppFormAuthenticationFilter());
filters.put("mperms", new FramePermissionsAuthorizationFilter(shiroProperties.isAuthz()));
shiroFilterFactoryBean.setFilters(filters);
//拦截器.
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
// //静态文件
// filterChainDefinitionMap.put("/assets/**", "anon");
// filterChainDefinitionMap.put("/css/**", "anon");
// filterChainDefinitionMap.put("/fonts/**", "anon");
// filterChainDefinitionMap.put("/img/**", "anon");
// filterChainDefinitionMap.put("/js/**", "anon");
// filterChainDefinitionMap.put("/sampledata/**", "anon");
//
// //view页面
// filterChainDefinitionMap.put("/view/login", "anon");
// filterChainDefinitionMap.put("/view/login.html", "anon");
//
// //controller 访问
// filterChainDefinitionMap.put("/access/**", "anon");
// filterChainDefinitionMap.put("/logout", "logout");
//
// filterChainDefinitionMap.put("/**", "fLogin, mauthc, mperms");
//通过加载properties文件实现
OrderedPropertiesReader opReader = OrderedPropertiesReader.getInstance();
Map<String, String> propertyMap = opReader.getPropertyMap("shiroFilterChainDefinition.properties");
filterChainDefinitionMap.putAll(propertyMap);
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
/**
* 会话Cookie模板
*/
@Bean("sessionIdCookie")
@ConfigurationProperties("spring.shiro.session-id-cookie")
public Cookie sessionIdCookie(){
return new SimpleCookie("yui2-token");//默认为yui2-token
}
/**
* 会话ID生成器
*/
@Bean("sessionIdGenerator")
public SessionIdGenerator sessionIdGenerator(){
return new JavaUuidSessionIdGenerator();
}
/**
* Redis 实现 ShiroSessionDao
*/
@Bean("sessionDAO")
@ConfigurationProperties("spring.shiro.session-dao")
public SessionDAO sessionDAO(RedisRepository redisRepository, SessionIdGenerator sessionIdGenerator){
RedisShiroSessionDao redisShiroSessionDao = new RedisShiroSessionDao();
redisShiroSessionDao.setRedisRepository(redisRepository);
redisShiroSessionDao.setSessionIdGenerator(sessionIdGenerator);
return redisShiroSessionDao;
}
@Bean("cacheManager")
public CacheManager cacheManager(RedisRepository redisRepository){
ShiroRedisCacheManager cacheManager = new ShiroRedisCacheManager();
cacheManager.setRedisRepository(redisRepository);
return cacheManager;
}
@Bean("sessionManager")
@ConfigurationProperties("spring.shiro.session-mgr")
public SessionManager sessionManager(SessionDAO sessionDAO, Cookie sessionIdCookie){
WebAndAppSessionManager sessionManager = new WebAndAppSessionManager();
sessionManager.setSessionValidationSchedulerEnabled(false);
sessionManager.setSessionDAO(sessionDAO);
sessionManager.setSessionIdCookie(sessionIdCookie);
return sessionManager;
}
@Bean("subjectFactory")
public SubjectFactory subjectFactory(){
return new DefaultWebSubjectFactory();
}
@Bean("securityManager")
public SecurityManager securityManager(SessionManager sessionManager, Realm shiroDBRealm,
SubjectFactory subjectFactory, CacheManager cacheManager){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setSessionManager(sessionManager);
securityManager.setRealm(shiroDBRealm);
securityManager.setSubjectFactory(subjectFactory);
securityManager.setCacheManager(cacheManager);
return securityManager;
}
@Bean("shiroDBRealm")
@ConfigurationProperties("spring.shiro.shiro-db-realm")
public Realm shiroDBRealm(){
ShiroDBRealm shiroDBRealm = new ShiroDBRealm();
shiroDBRealm.setAuthenticationCacheName("authenticationCache");
shiroDBRealm.setAuthorizationCacheName("authorizationCache");
return shiroDBRealm;
}
@Bean("lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
@Bean("methodInvokingFactoryBean")
public MethodInvokingFactoryBean methodInvokingFactoryBean(SecurityManager securityManager) {
MethodInvokingFactoryBean factoryBean = new MethodInvokingFactoryBean();
factoryBean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
factoryBean.setArguments(new Object[]{securityManager});
return factoryBean;
}
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();
proxyCreator.setProxyTargetClass(true);
return proxyCreator;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
}
Shiro 用户信息和授权信息,缓存配置,这里使用了redis缓存
import java.util.Collection;
import java.util.Set;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import yui.comn.redis.RedisRepository;
import yui.comn.utils.SerializeUtils;
/**
* Redis 实现Shiro缓存
* @author yuyi
*/
public class ShiroRedisCache<K, V> implements Cache<K, V> {
public static Logger logger = LoggerFactory.getLogger(ShiroRedisCache.class);
private String name;
private RedisRepository redisRepository;
public ShiroRedisCache(String name, RedisRepository redisRepository) {
this.name = name;
this.redisRepository = redisRepository;
}
private byte[] getByteKey(K key) {
if (key instanceof String) {
String preKey = key.toString();
return preKey.getBytes();
} else {
return SerializeUtils.serialize(key);
}
}
private byte[] getByteName() {
return name.getBytes();
}
@Override
public void clear() throws CacheException {
logger.debug("从redis中删除所有元素");
try {
redisRepository.del(getByteName());
} catch (Throwable t) {
throw new CacheException(t);
}
}
@SuppressWarnings("unchecked")
@Override
public V get(K key) throws CacheException {
logger.debug("根据key从Redis中获取对象 key [" + key + "]");
try {
if (key == null) {
return null;
} else {
V value = (V) redisRepository.hGet(getByteName(), getByteKey(key));
return value;
}
} catch (Throwable t) {
throw new CacheException(t);
}
}
@SuppressWarnings("unchecked")
@Override
public Set<K> keys() {
try {
Set<K> keys = redisRepository.hKeys(getByteName());
return keys;
} catch (Throwable t) {
throw new CacheException(t);
}
}
@Override
public V put(K key, V val) throws CacheException {
logger.debug("根据key从存储 key [" + key + "]");
try {
redisRepository.hPut(getByteName(), getByteKey(key), SerializeUtils.serialize(val));
return val;
} catch (Throwable t) {
throw new CacheException(t);
}
}
@Override
public V remove(K key) throws CacheException {
logger.debug("从redis中删除 key [" + key + "]");
try {
V previous = get(key);
redisRepository.hDel(getByteName(), getByteKey(key));
return previous;
} catch (Throwable t) {
throw new CacheException(t);
}
}
@Override
public int size() {
try {
Long longSize = new Long(redisRepository.hLen(getByteName()));
return longSize.intValue();
} catch (Throwable t) {
throw new CacheException(t);
}
}
@SuppressWarnings("unchecked")
@Override
public Collection<V> values() {
try {
Collection<V> values = redisRepository.hVals(getByteName());
return values;
} catch (Throwable t) {
throw new CacheException(t);
}
}
}
import org.apache.shiro.cache.AbstractCacheManager;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import yui.comn.redis.RedisRepository;
/**
* redis 缓存管理器
* @author yuyi
*/
public class ShiroRedisCacheManager extends AbstractCacheManager {
private RedisRepository redisRepository;
@Override
protected Cache<String, Object> createCache(String cacheName) throws CacheException {
return new ShiroRedisCache<String, Object>(cacheName, redisRepository);
}
public RedisRepository getRedisRepository() {
return redisRepository;
}
public void setRedisRepository(RedisRepository redisRepository) {
this.redisRepository = redisRepository;
}
}
Shiro 基于redis存储的SessionDao
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.SimpleSession;
import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import yui.comn.redis.RedisRepository;
import yui.comn.utils.DateUtils;
/**
* Redis实现的 ShiroSessionDao
* @author yuyi
*/
public class RedisShiroSessionDao extends AbstractSessionDAO {
public Logger logger = LoggerFactory.getLogger(RedisShiroSessionDao.class);
private String sessionPrefix = "yui2-sid";
private Long expire = 28800L; //28800L;
private RedisRepository redisRepository;
@Override
public void update(Session session) throws UnknownSessionException {
try {
SimpleSession simpleSession = (SimpleSession) session;
simpleSession.setStartTimestamp(DateUtils.currentTimestamp());
long expireSec = null != expire ? expire : session.getTimeout()/1000;
redisRepository.put(getTbSid(session), session, expireSec);
} catch (Exception e) {
logger.error("更新session失败", e);
}
}
@Override
public void delete(Session session) {
try {
redisRepository.del(getTbSid(session));
} catch (Exception e) {
logger.error("删除session失败", e);
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public Collection<Session> getActiveSessions() {
String pattern = getTbSid(sessionPrefix + "-*");
List list = null;
try {
Set keys = redisRepository.keys(pattern);
if (null != keys) {
list = new ArrayList<Object>(keys);
}
} catch (Exception e) {
logger.error("获取有效session失败", e);
}
return list;
}
@Override
protected Serializable doCreate(Session session) {
Serializable sessionId = session.getId();
try {
super.assignSessionId(session, sessionPrefix + "-" + super.generateSessionId(session));
update(session);
sessionId = session.getId();
} catch (Exception e) {
logger.error("创建session失败", e);
}
return sessionId;
}
@Override
protected Session doReadSession(Serializable sessionId) {
Session session = null;
try {
session = (Session) redisRepository.get(getTbSid(sessionId));
} catch (Exception e) {
logger.error("读取session失败", e);
}
return session;
}
public String getTbSid(Session session) {
if (null == session.getId()) {
return null;
}
return getTbSid(session.getId());
}
public String getTbSid(String sid) {
return new StringBuffer(sessionPrefix).append(":").append(sid).toString();
}
public String getTbSid(Serializable sid) {
return getTbSid(sid.toString());
}
public void setSessionPrefix(String sessionPrefix) {
this.sessionPrefix = sessionPrefix;
}
public void setExpire(Long expire) {
this.expire = expire;
}
public void setRedisRepository(RedisRepository redisRepository) {
this.redisRepository = redisRepository;
}
}
Shiro 自己覆写实现类,WebAndAppSessionManager继承DefaultWebSessionManager,如果header带有token信息,先从header中获取token信息。
如果header没有带有token信息,再去shiro默认实现中去获取。
Shiro默认先去cookie中去获取,如果cookie中没有token,再去参数中去获取是否带有token参数信息。
import java.io.Serializable;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
/**
* @author yuyi
*/
public class WebAndAppSessionManager extends DefaultWebSessionManager {
@Override
protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
Cookie sessionIdCookie = getSessionIdCookie();
HttpServletRequest httpReq = (HttpServletRequest) request;
String shiroToken = httpReq.getHeader(sessionIdCookie.getName());
if (null != shiroToken) {
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE,
ShiroHttpServletRequest.URL_SESSION_ID_SOURCE);
return shiroToken;
}
return super.getSessionId(request, response);
}
}
Shiro 实现ShiroDBRealm类,实现认证和授权方法,和清除缓存方法。
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.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.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import yui.comn.shiro.mgr.IUserDetailMgrx;
import yui.comn.shiro.utils.ShiroConstant;
import yui.comn.utils.SpringContextUtils;
import yui.comn.utils.UserInfo;
/**
* @author yuyi
*/
//@Service
public class ShiroDBRealm extends AuthorizingRealm {
private IUserDetailMgrx userDetailMgrx;
private IUserDetailMgrx getUserDetailMgrx() {
//解决shiro不能注入dubbo的bug
if (null == userDetailMgrx) {
userDetailMgrx = (IUserDetailMgrx) SpringContextUtils.getBean(IUserDetailMgrx.USER_DETAIL_MGRX);
}
return userDetailMgrx;
}
public void setUserDetailMgrx(IUserDetailMgrx userDetailMgrx) {
this.userDetailMgrx = userDetailMgrx;
}
/**
* 认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
throws AuthenticationException {
String principal = token.getPrincipal().toString();
//forceLogoutBefore(principal);
UserInfo userInfo = getUserDetailMgrx().getUserInfo(principal);
PrincipalCollection principalCollection = new SimplePrincipalCollection(userInfo, ShiroConstant.AUTHORIZING_REALM_NAME);
return new SimpleAuthenticationInfo(principalCollection, token.getCredentials());
}
/**
* 强制退出之前的账号
* @param username
*/
@SuppressWarnings("unused")
private void forceLogoutBefore(String username) {
DefaultWebSecurityManager securityManager = (DefaultWebSecurityManager) SecurityUtils.getSecurityManager();
DefaultWebSessionManager sessionManager = (DefaultWebSessionManager) securityManager.getSessionManager();
Collection<Session> activeSessions = sessionManager.getSessionDAO().getActiveSessions();
for (Session session : activeSessions) {
SimplePrincipalCollection principals = (SimplePrincipalCollection) session.getAttribute(
DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
UserInfo userInfo = (UserInfo) principals.getPrimaryPrincipal();
if (StringUtils.equalsIgnoreCase(userInfo.getUsername(), username)) {
if (null == session.getAttribute(ShiroConstant.SESSION_FORCE_LOGOUT_KEY)) {
session.setAttribute(ShiroConstant.SESSION_FORCE_LOGOUT_KEY, Boolean.TRUE);
sessionManager.getSessionDAO().update(session);
}
}
}
}
/**
* 授权
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
UserInfo userInfo = (UserInfo) principals.getPrimaryPrincipal(); //从这里可以从cas server获得认证通过的用户名,得到后我们可以根据用户名进行具体的授权
Map<String, Set<String>> roleAndPermMap = getUserDetailMgrx().getRoleAndPerm(userInfo.getSysUserPk());
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
authorizationInfo.setRoles(roleAndPermMap.get(IUserDetailMgrx.ROLE_MAP_KEY));
authorizationInfo.setStringPermissions(roleAndPermMap.get(IUserDetailMgrx.PERM_MAP_KEY));
return authorizationInfo;
}
@Override
public AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals) {
return super.getAuthorizationInfo(principals);
}
/**
* 覆写认证缓存key
*/
@Override
protected Object getAuthenticationCacheKey(PrincipalCollection principals) {
UserInfo userInfo = (UserInfo) principals.getPrimaryPrincipal();
return userInfo.getUsername();
//return super.getAuthenticationCacheKey(principals);
}
/**
* 覆写授权缓存key
*/
@Override
protected Object getAuthorizationCacheKey(PrincipalCollection principals) {
UserInfo userInfo = (UserInfo) principals.getPrimaryPrincipal();
return userInfo.getUsername();
//return super.getAuthenticationCacheKey(principals);
}
@Override
public void clearCache(PrincipalCollection principals) {
super.clearCache(principals);
}
/**
* 通过用户名清除缓存
*/
public void clearCache(String username) {
PrincipalCollection principals = new SimplePrincipalCollection(
new UserInfo(username), ShiroConstant.AUTHORIZING_REALM_NAME);
clearCache(principals);
}
/**
* 清除认证和授权缓存
*/
public void clearAllCache() {
clearAllAuthcCache();
clearAllAuthzCache();
}
/**
* 清除认证缓存
*/
public void clearAllAuthcCache() {
getAuthenticationCache().clear();
}
/**
* 清除授权缓存
*/
public void clearAllAuthzCache() {
getAuthorizationCache().clear();
}
}
/**
* @author yuyi
*/
public class ShiroConstant {
//realm名称
public static final String AUTHORIZING_REALM_NAME="shiroDbAuthorizingRealmName";
//强制退出标识
public static final String SESSION_FORCE_LOGOUT_KEY = "sessionForceLogoutKey";
}
Shiro 自己覆写实现类,自定义过滤去,WebAndAppFormAuthenticationFilter继承FormAuthenticationFilter,实现异步返回json数据结果,对于前后端分离,全部请求都返回json。
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Web And App FormAuthenticationFilter
* @author yuyi
*/
public class WebAndAppFormAuthenticationFilter extends FormAuthenticationFilter {
private static final Logger logger = LoggerFactory.getLogger(WebAndAppFormAuthenticationFilter.class);
private String noAuthenticationReturn = "{\"status\": \"401\", \"message\":\"未授权的访问,请检查Token后重试\"}";
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response)
throws Exception {
if (isLoginRequest(request, response)) {
if (isLoginSubmission(request, response)) {
if (logger.isTraceEnabled()) {
logger.trace("Login submission detected. Attempting to execute login.");
}
return executeLogin(request, response);
} else {
if (logger.isTraceEnabled()) {
logger.trace("Login page view.");
}
//allow them to see the login page ;)
return true;
}
} else {
if (logger.isTraceEnabled()) {
logger.trace("Attempting to access a path which requires authentication. Forwarding to the " +
"Authentication url [" + getLoginUrl() + "]");
}
response.setContentType("text/html;charset=UTF-8");
//如果header带有token信息
String refSessionIdSource = (String) request.getAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE);
if (ShiroHttpServletRequest.URL_SESSION_ID_SOURCE.equals(refSessionIdSource)) {
response.getWriter().write(noAuthenticationReturn);
} else {
//如果ajax异步请求
HttpServletRequest httpRequest = (HttpServletRequest) request;
String reqtType = httpRequest.getHeader("X-Requested-With");
if (null != reqtType) {
response.getWriter().write(noAuthenticationReturn);
} else {
//因为都是异步请求
response.getWriter().write(noAuthenticationReturn);
//saveRequestAndRedirectToLogin(request, response);
}
}
return false;
}
}
public void setNoAuthenticationReturn(String noAuthenticationReturn) {
this.noAuthenticationReturn = noAuthenticationReturn;
}
}
Shiro 自己覆写实现类,自定义过滤去,ForceLogoutFilter继承AccessControlFilter,如果有用户被强制退出,该用户再次访问时会返回"{\"status\": \"402\", \"msg\":\"被强制退出\"}",表示用户被强制退出,去要再次登陆。
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.apache.shiro.session.Session;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import yui.comn.shiro.utils.ShiroConstant;
/**
* 强制退出
* @author yuyi
*/
public class ForceLogoutFilter extends AccessControlFilter {
private static final Logger logger = LoggerFactory.getLogger(ForceLogoutFilter.class);
@SuppressWarnings("unused")
private String redirectUrl; //= "http://127.0.0.1:8080/cas/logout?service=http://127.0.0.1:8181/adm/";
private String forceLogoutReturn = "{\"status\": \"402\", \"msg\":\"被强制退出\"}";
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response,
Object mappedValue) throws Exception {
Session session = getSubject(request, response).getSession(false);
if(null == session) {
return true;
}
return null == session.getAttribute(ShiroConstant.SESSION_FORCE_LOGOUT_KEY);
}
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response)
throws Exception {
if (logger.isTraceEnabled()) {
logger.trace("Attempting to access a path which force logout. Forwarding to the " +
"Authentication url [" + getLoginUrl() + "]");
}
HttpServletRequest httpRequest = (HttpServletRequest) request;
String refSessionIdSource = (String) request.getAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE);
if (ShiroHttpServletRequest.URL_SESSION_ID_SOURCE.equals(refSessionIdSource)) {
logout(request, response);
response.getWriter().write(forceLogoutReturn);
} else {
String reqtType = httpRequest.getHeader("X-Requested-With");
if (null != reqtType) {
response.getWriter().write(forceLogoutReturn);
} else {
logout(request, response);
saveRequest(request);
//WebUtils.issueRedirect(request, response, redirectUrl);
saveRequestAndRedirectToLogin(request, response);
}
}
return false;
}
//删除session
private void logout(ServletRequest request, ServletResponse response) throws ServletException, IOException {
//Exception exception = null;
try {
getSubject(request, response).logout();//强制退出
} catch (Exception e) {
logger.error("强制退出失败", e);
} finally {
//cleanup(request, response, exception);
}
}
public void setRedirectUrl(String redirectUrl) {
this.redirectUrl = redirectUrl;
}
public void setForceLogoutReturn(String forceLogoutReturn) {
this.forceLogoutReturn = forceLogoutReturn;
}
}
Shiro 自己覆写实现类,自定义过滤去,FramePermissionsAuthorizationFilter继承PermissionsAuthorizationFilter,进行授权过滤,默认不开启,如果开启会对每个接口进行授权。
import java.io.IOException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author yuyi
*/
public class FramePermissionsAuthorizationFilter extends PermissionsAuthorizationFilter {
private static Logger logger = LoggerFactory.getLogger(FramePermissionsAuthorizationFilter.class);
private boolean isAuthz;
public FramePermissionsAuthorizationFilter() {
super();
}
public FramePermissionsAuthorizationFilter(boolean isAuthz) {
super();
this.isAuthz = isAuthz;
}
@Override
public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {
if (!isAuthz) {
return true;
}
HttpServletRequest req = (HttpServletRequest) request;
Subject subject = getSubject(request, response);
String uri = req.getRequestURI();
String contextPath = req.getContextPath();
int index = uri.indexOf(contextPath);
if(index > -1){
uri = uri.substring(index + contextPath.length());
}
//首页免登录
if (StringUtils.equals("/", uri)) {
return true;
}
//阿里druid数据源默认全部允许
if (StringUtils.indexOf(uri, "/druid") == 0) {
return true;
}
//如果不是菜单,变成shiro资源格式
if (!StringUtils.contains(uri, ".")) {
uri = StringUtils.substring(uri, 1);
uri = StringUtils.replace(uri, "/", ":");
}
boolean permitted = subject.isPermitted(uri);
if (!permitted) {
logger.warn("uri[" + uri + "] is no perm");
}
return permitted;
}
public boolean isAuthz() {
return isAuthz;
}
public void setAuthz(boolean isAuthz) {
this.isAuthz = isAuthz;
}
}