關於shiro授權不呼叫(或者需要重啟伺服器才生效)的陷阱
阿新 • • 發佈:2019-01-07
關於shiro的配置我不多說了,現在說一下更改許可權後要重啟伺服器的問題
web.xml
<filter> <filter-name>myShiroFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <param-name>targetFilterLifecycle</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>myShiroFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
ShiroConfig.java
@Bean(name = "myShiroFilter") public ShiroFilterFactoryBean myShiroFilter() { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); //設定 SecurityManager shiroFilterFactoryBean.setSecurityManager(securityManager); //設定登入連結(前後端分離方案中這裡不返回頁面,返回403報文,供前端跳轉到登入頁面) //如果不配置,預設跳到登入頁面 shiroFilterFactoryBean.setLoginUrl("/403"); // 登入成功後要跳轉的連結(前後端分離方案這個不需要) //shiroFilterFactoryBean.setSuccessUrl("/home"); // 未授權跳轉連結; shiroFilterFactoryBean.setUnauthorizedUrl("/401"); //攔截鏈配置 Map<String, String> filterChainDefinitionMap = constructFilterChainDefinitionMap(); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); log.info("ShiroFilterFactoryBean注入成功!"); return shiroFilterFactoryBean; }
/** * 構造shiro過濾鏈配置 * */ private Map<String,String> constructFilterChainDefinitionMap(){ Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>(); //動態載入url許可權配置,從資料庫獲取 List<SysAccessPermissionTest> list = sysAccessPermissionServiceImpl.getAll(); for (SysAccessPermissionTest item : list) { filterChainDefinitionMap.put(item.getUrl(),item.getRoles()); } return filterChainDefinitionMap; }
ShiroConfigServer.java的更新許可權的方法
public void updateMyPermission() {
synchronized (shiroFilterFactoryBean) {
AbstractShiroFilter shiroFilter = null;
try {
shiroFilter = (AbstractShiroFilter) shiroFilterFactoryBean
.getObject();
} catch (Exception e) {
throw new RuntimeException(
"get ShiroFilter from shiroFilterFactoryBean error!");
}
PathMatchingFilterChainResolver filterChainResolver = (PathMatchingFilterChainResolver) shiroFilter
.getFilterChainResolver();
DefaultFilterChainManager manager = (DefaultFilterChainManager) filterChainResolver
.getFilterChainManager();
// 清空老的許可權控制
manager.getFilterChains().clear();
shiroFilterFactoryBean.getFilterChainDefinitionMap().clear();
shiroFilterFactoryBean.setFilterChainDefinitionMap(loadFilterChainDefinitions());
// 重新構建生成
Map<String, String> chains = shiroFilterFactoryBean.getFilterChainDefinitionMap();
//System.out.println("chains-------"+chains);
for (Map.Entry<String, String> entry : chains.entrySet()) {
String url = entry.getKey();
String chainDefinition = entry.getValue().trim().replace(" ", "");
manager.createChain(url, chainDefinition);
}
log.info("更新許可權成功!!");
}
}
注意:
updateMyPermission()的方法是更新許可權的操作,紅色的部分要去掉,這樣才可以不用重啟服務,ShiroConfigServer.java 完整的方法
package com.gameloft9.demo.security;
import com.gameloft9.demo.service.api.system.SysAccessPermissionService;
import com.gameloft9.demo.dataaccess.model.system.SysAccessPermissionTest;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
import org.apache.shiro.web.servlet.AbstractShiroFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.support.WebApplicationContextUtils;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
/**
* shiro 動態許可權配置相關服務
* @author gameloft9
*/
@Service
@Slf4j
public class ShiroConfigService {
@Autowired
ShiroFilterFactoryBean shiroFilterFactoryBean;
@Autowired
SysAccessPermissionService sysAccessPermissionServiceImpl;
/**
* 從資料庫載入許可權列表
*/
public Map<String, String> loadFilterChainDefinitions() {
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
List<SysAccessPermissionTest> list = sysAccessPermissionServiceImpl.getAll();
for (SysAccessPermissionTest item : list) {
filterChainDefinitionMap.put(item.getUrl(), item.getRoles());
}
return filterChainDefinitionMap;
}
/**
* 更新許可權(已廢棄),會造成要重啟才能生效許可權
*/
@Deprecated
public void updateMyPermission() {
synchronized (shiroFilterFactoryBean) {
AbstractShiroFilter shiroFilter = null;
try {
shiroFilter = (AbstractShiroFilter) shiroFilterFactoryBean
.getObject();
} catch (Exception e) {
throw new RuntimeException(
"get ShiroFilter from shiroFilterFactoryBean error!");
}
PathMatchingFilterChainResolver filterChainResolver = (PathMatchingFilterChainResolver) shiroFilter
.getFilterChainResolver();
DefaultFilterChainManager manager = (DefaultFilterChainManager) filterChainResolver
.getFilterChainManager();
// 清空老的許可權控制
manager.getFilterChains().clear();
shiroFilterFactoryBean.getFilterChainDefinitionMap().clear();
shiroFilterFactoryBean.setFilterChainDefinitionMap(loadFilterChainDefinitions());
// 重新構建生成
Map<String, String> chains = shiroFilterFactoryBean.getFilterChainDefinitionMap();
//System.out.println("chains-------"+chains);
for (Map.Entry<String, String> entry : chains.entrySet()) {
String url = entry.getKey();
String chainDefinition = entry.getValue().trim().replace(" ", "");
manager.createChain(url, chainDefinition);
}
log.info("更新許可權成功!!");
}
}
/**
* 更新許可權,解決需要重啟tomcat才能生效許可權的問題
*/
public void updatePermission() {
try {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
ServletContext servletContext = request.getSession().getServletContext();
AbstractShiroFilter shiroFilter = (AbstractShiroFilter) WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext).getBean("myShiroFilter");
//ShiroFilterFactoryBean shiroFilter = (ShiroFilterFactoryBean) WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext).getBean("myShiroFilter");
synchronized (shiroFilter) {
// 獲取過濾管理器
PathMatchingFilterChainResolver filterChainResolver = (PathMatchingFilterChainResolver) shiroFilter.getFilterChainResolver();
DefaultFilterChainManager manager = (DefaultFilterChainManager) filterChainResolver.getFilterChainManager();
// 清空初始許可權配置
manager.getFilterChains().clear();
// 重新獲取資源
Map<String, String> chains = loadFilterChainDefinitions();
// System.out.println("chains-------"+chains);
for (Map.Entry<String, String> entry : chains.entrySet()) {
String url = entry.getKey();
String chainDefinition = entry.getValue().trim().replace(" ", "");
//System.out.println(url+"\t-----> "+chainDefinition);
manager.createChain(url, chainDefinition);
}
log.info("更新許可權成功!!");
}
} catch (Exception e) {
log.error(e.getMessage());
}
}
}
再說一下不走授權doGetAuthorizationInfo方法,我的ShiroRealm完整檔案如下
package com.gameloft9.demo.security;
import com.gameloft9.demo.service.api.system.SysUserService;
import com.gameloft9.demo.dataaccess.model.system.UserTest;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import java.util.List;
/**
* 認證授權。
* @author gameloft9
*/
@Slf4j
@Data
public class ShiroRealm extends AuthorizingRealm {
/**
* 通過setter注入,這裡沒有通過@Autowired注入
* */
private SysUserService userServiceImpl;
/**
* 獲取授權資訊方法,返回使用者角色資訊
* */
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals) {
if (principals == null) {
throw new AuthorizationException("PrincipalCollection method argument cannot be null.");
}
UserTest user = (UserTest) principals.getPrimaryPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
if (user != null) {//獲取使用者角色資訊
List<String> roles = userServiceImpl.getRoleNames(user.getId());
info.addRoles(roles);
} else {
SecurityUtils.getSubject().logout();
}
return info;
}
/**
* 重寫回調認證方法,subject.login()呼叫後回撥此方法,獲取認證資訊。
* 如果是與第三方使用者系統整合,可在此處進行身份認證,成功後可構造一個同登入token一致的認證資訊。
* 或者乾脆跳過shiro的認證,自己實現認證邏輯,成功後將使用者資訊放入session、cookie.
* */
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken authcToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
UserTest user = userServiceImpl.getByLoginName(token.getUsername());
if (user == null) {//使用者不存在
throw new UnknownAccountException();
}
//構造一個使用者認證資訊並返回,後面會通過這個和token的pwd進行對比。
return new SimpleAuthenticationInfo(user,user.getPassword(),user.getRealName());
}
}
網上所說的都不適用,例如:
1、subject.hasRole(“admin”) 或 subject.isPermitted(“admin”):自己去呼叫這個是否有什麼角色或者是否有什麼許可權的時候;
2、@RequiresRoles("admin") :在方法上加註解的時候;
3、[@shiro.hasPermission name = "admin"][/@shiro.hasPermission]:在頁面上加shiro標籤的時候,即進這個頁面的時候掃描到有這個標籤的時候。
在所有的程式碼檢查完後,如果不是這方面的原因,很可能是你配置的許可權url規則有問題,請看圖
小夥伴們發現了什麼問題了嗎,下面的是執行後臺請求的路徑字首,例如sysUser/list.do,一定要在前面加/
沒錯就是url配置的問題,字首一定加/,所以路徑很重要,重要的事情再說一遍,字首一定要加/,否則你的攔截會失效!!!!