用戶A在線,管理員在後臺更改了用戶A信息(資料或權限)以後;用戶A再進行下一步操做時,會被攔截並退出登陸狀態,再登陸才能夠執行操做;來確保用戶A的信息同步更新。html
版本升級及內容優化版本,改動內容:java
前篇: linux
基於前篇,新增功能:nginx
wyait-manage、wyait-manage-1.2.0源碼都更新了以上功能!git
以及新增了springboot項目,開發和線上jdk版本不一致致使項目沒法啓動、沒法加載的問題的排查及解決思路。github
後篇: web
github源碼: https://github.com/wyait/manage.git
碼雲:https://gitee.com/wyait/manage.git
github對應項目源碼目錄:wyait-manage-1.2.0
碼雲對應項目源碼目錄:wyait-manage-1.2.0 redis
使用shiro可能會遇到修改了用戶權限後,沒有當即生效,須要等到用戶從新登陸後才能生效;不能當即同步更新,顯然是不合理的。spring
【實測無效】!!!
受權方法,是在shiro進行鑑權的時候才能觸發。只是配置了authc/user/anon等,不會觸發;
perms,port,rest,roles,ssl等,會觸發受權方法doGetAuthorizationInfo。 數據庫
/** * 清除全部緩存 */ public void clearCachedAuth(){ this.clearCachedAuthorizationInfo(SecurityUtils.getSubject().getPrincipals()); }
//清除ehcache中全部用戶權限緩存 RealmSecurityManager rsm = (RealmSecurityManager)SecurityUtils.getSecurityManager(); ShiroRealm authRealm = (ShiroRealm)rsm.getRealms().iterator().next(); authRealm.clearCachedAuth();
實際解決方案參考下文中的方案二!
在系統中,由管理員更改了用戶A信息後,若是用戶A在線,沒法及時更新相關的改動;
更新用戶資料、權限等信息,若是該用戶在線,同步更新用戶信息解決方案:
在ShiroRealm中經過SessionDAO拿到全部在線的用戶,
Collection<Session> sessions = sessionDAO.getActiveSessions();
遍歷找到匹配的,根據狀況,退出登陸或更新用戶信息:
@Autowired private SessionDAO sessionDAO; public void updateShiroUser(String loginName){ Collection<Session> sessions = sessionDAO.getActiveSessions(); for(Session session:sessions){ if(loginName.equals(String.valueOf(session.getAttribute(DefaultSubjeContext.PRINCIPALS_SESSION_KEY))) { //設置session當即失效,即將其踢出系統 session.setTimeout(0); //TODO 或更新下用戶信息 break; } }
【不推薦理由】
用戶信息新增version版本標記,寫個攔截器,每次請求判斷version是否一致,若有改動,根據狀況,退出或更新用戶信息(本文統一作了退出登陸處理,能夠結合實際需求作相應調整)。
這個方案,基於樂觀鎖原理實現。一樣可解決動態更新用戶權限的問題。
ALTER TABLE `user` MODIFY COLUMN `id` int(10) NOT NULL AUTO_INCREMENT FIRST , ADD COLUMN `version` int(10) NULL DEFAULT 0 COMMENT '更新版本' AFTER `send_time`;
TODO【詳見源碼】
/** * * @項目名稱:wyait-manage * @類名稱:UserActionInterceptor * @類描述:判斷用戶信息是否已被後臺更改,並根據更改的狀況作對應的處理 * @建立人:wyait * @建立時間:2018年5月2日 上午9:36:43 * @version: */ public class UserActionInterceptor implements HandlerInterceptor { private static Logger logger = LoggerFactory .getLogger(UserActionInterceptor.class); @Autowired private UserService userService; ... ... @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object obj, Exception e) throws Exception { // TODO Auto-generated method stub } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object obj, ModelAndView mv) throws Exception { // TODO Auto-generated method stub } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object obj) throws Exception { // TODO Auto-generated method stub logger.debug("請求到達後臺方法以前調用(controller以前)"); // 1. SecurityUtils獲取session中的用戶信息 // HttpSession session=request.getSession(); User user = (User) SecurityUtils.getSubject().getPrincipal(); if (user != null && StringUtils.isNotEmpty(user.getMobile()) && null != user.getVersion()) { // 2. 獲取數據庫中的用戶數據 User dataUser = this.userService.findUserByMobile(user.getMobile()); // 3. 對比session中用戶的version和數據庫中的是否一致 if (dataUser != null && null != dataUser.getVersion() && String.valueOf(user.getVersion()).equals( String.valueOf(dataUser.getVersion()))) { // 3.1 同樣,放行 return true; }else{ // 3.2 不同,這裏統一作退出登陸處理;//TODO 使用redis緩存用戶權限數據,根據用戶更新、用戶權限更新;作對應的處理。 SecurityUtils.getSubject().logout(); isAjaxResponse(request,response); } } return false; } ... ... }
/** * * @項目名稱:wyait-manage * @類名稱:MyWebMvcConfig * @類描述:自定義靜態資源映射路徑和靜態資源存放路徑 * @建立人:wyait * @修改時間:2018年5月3日09:55:23 * @version: */ @Configuration public class MyWebMvcConfig extends WebMvcConfigurerAdapter { /** * 添加攔截器 */ @Override public void addInterceptors(InterceptorRegistry registry) { // 路徑根據後期項目的擴展,進行調整 registry.addInterceptor(new UserActionInterceptor()) .addPathPatterns("/user/**", "/auth/**") .excludePathPatterns("/user/sendMsg", "/user/login"); super.addInterceptors(registry); } }
錯誤信息:
java.lang.NullPointerException: null at com.wyait.manage.interceptor.UserActionInterceptor.preHandle(UserActionInterceptor.java:62) ~[classes/:?] at org.springframework.web.servlet.HandlerExecutionChain.applyPreHandle(HandlerExecutionChain.java:133) ~[spring-webmvc-4.3.13.RELEASE.jar:4.3.13.RELEASE] at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:962) ~[spring-webmvc-4.3.13.RELEASE.jar:4.3.13.RELEASE]
userService對象爲null,沒法注入UserService。
@Autowired private UserService userService;
@Configuration public class MyWebMvcConfig extends WebMvcConfigurerAdapter { /** * * @描述:在Spring添加攔截器以前先建立攔截器對象,這樣就能在Spring映射這個攔截器前,把攔截器中的依賴注入的對象給初始化完成了。 * </br>避免攔截器中注入的對象爲null問題。 * @建立人:wyait * @建立時間:2018年5月3日 上午10:07:36 * @return */ @Bean public UserActionInterceptor userActionInterceptor(){ return new UserActionInterceptor(); } /** * 添加攔截器 */ @Override public void addInterceptors(InterceptorRegistry registry) { // 路徑根據後期項目的擴展,進行調整 registry.addInterceptor(userActionInterceptor()) .addPathPatterns("/user/**", "/auth/**") .excludePathPatterns("/user/sendMsg", "/user/login"); super.addInterceptors(registry); } }
分佈式或集羣的時候,須要解決session共享問題;相關的方案有:session持久化、redis或其餘中間件、nginx的ip_hash、cookie實現、服務器間Session同步等;這時候處理動態更新用戶信息,須要結合實際狀況而定;
後期會更新redis版本。
linux 6.* 系統
jdk 1.7
tomcat 7.*
maven 3.3.3
五月 08, 2018 11:49:23 上午 org.apache.catalina.startup.TaglibUriRule body 信息: TLD skipped. URI: http://shiro.apache.org/tags is already defined 五月 08, 2018 11:49:23 上午 org.apache.catalina.startup.TldConfig execute 信息: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time. 五月 08, 2018 11:51:03 上午 org.apache.catalina.util.SessionIdGeneratorBase createSecureRandom 信息: Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [112,917] milliseconds. 五月 08, 2018 11:51:03 上午 org.apache.catalina.startup.HostConfig deployDirectory 信息: Deployment of web application directory /usr/tools/tomcat-9190/webapps/ROOT has finished in 117,517 ms 五月 08, 2018 11:51:03 上午 org.apache.catalina.startup.Catalina start 信息: Server startup in 117563 ms 五月 08, 2018 11:51:37 上午 org.apache.catalina.util.SessionIdGeneratorBase createSecureRandom 信息: Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [134,439] milliseconds. ...
無明顯錯誤,可是項目沒有加載成功,訪問是404.
At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
對比本地tomcat7和線上tomcat7啓動,會出現tomcat7和8版本衝突問題,本地解決了,線上依舊啓動不成功;再百度、google方案,大體是tomcat版本問題而打印的日誌信息,按照搜索的方案配置,均未解決。
考慮應該是環境配置的版本不一致致使的項目沒法加載啓動成功,確認並排查下開發、測試、線上的jdk、tomcat等版本是否一致。
java -version
jdk 1.8
java -v
jdk 1.7
這就是致使項目在linux系統啓動不起來的緣由:開發和線上的jdk版本不一致!!!
將windows的jdk版本切換爲jdk1.7,從新打開新的dos窗口:java -version;jdk顯示爲1.7.*。版本切換成功!
... [ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3. 1:compile (default-compile) on project wyait-manage: Fatal error compiling: 無效的 目標發行版: 1.8 -> [Help 1] [ERROR] [ERROR] To see the full stack trace of the errors, re-run Maven with the -e swit ch. ...
<!-- <profile> <id>jdk-1.8</id> <activation> <activeByDefault>true</activeByDefault> <jdk>1.8</jdk> </activation> <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion> </properties> </profile> -->
註釋掉這一段maven指定jdk爲1.8版本的配置;從新打包,成功!
開發過程當中,要確保開發、測試、線上配置的環境(jdk、maven、tomcat等開發依賴的環境支持)保持一致。避免出現因爲開發環境中的版本不一致而出現問題,致使項目上線出問題和延遲項目上線時間!
版本升級及內容優化版本,改動內容:
前篇:
後篇:
項目源碼:(包含數據庫源碼)
github源碼: https://github.com/wyait/manage.git
碼雲:https://gitee.com/wyait/manage.git
github對應項目源碼目錄:wyait-manage-1.2.0
碼雲對應項目源碼目錄:wyait-manage-1.2.0