shiro配置 在springboot中前後端分離中,整合shiro認證授權框架
一:介紹
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 [email protected]
*/
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 [email protected]
*/
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 [email protected]
*/
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 [email protected]
*/
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 [email protected]
*/
//@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 [email protected]
*/
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 [email protected]
*/
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 [email protected]
*/
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 [email protected]
*/
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;
}
}