溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

spring boot + shiro 動態(tài)更新用戶信息

發(fā)布時間:2020-09-21 03:49:48 來源:網(wǎng)絡(luò) 閱讀:64065 作者:wyait 欄目:開發(fā)技術(shù)

spring boot + shiro 動態(tài)更新用戶信息

場景

用戶A在線,管理員在后臺更改了用戶A信息(資料或權(quán)限)之后;用戶A再進(jìn)行下一步操作時,會被攔截并退出登錄狀態(tài),再登錄才可以執(zhí)行操作;來確保用戶A的信息同步更新。

后臺權(quán)限管理系統(tǒng)

技術(shù)實現(xiàn)

前篇:

  • spring boot + mybatis + layui + shiro后臺權(quán)限管理系統(tǒng):https://blog.51cto.com/wyait/2082803
  • springboot + shiro之登錄人數(shù)限制、登錄判斷重定向、session時間設(shè)置:https://blog.51cto.com/wyait/2107423

基于前篇,新增功能:

  1. 新增用戶表版本version字段;
  2. 更新用戶操作時,通過version字段來保證數(shù)據(jù)一致;
  3. 新增通過攔截器實現(xiàn)動態(tài)更新用戶信息(用戶資料、用戶權(quán)限);
  4. 新增登錄成功后默認(rèn)頁面home.html;
  5. 頁面操作細(xì)節(jié)優(yōu)化

wyait-manage、wyait-manage-1.2.0源碼都更新了以上功能!

以及新增了springboot項目,開發(fā)和線上jdk版本不一致導(dǎo)致項目無法啟動、無法加載的問題的排查及解決思路。

后篇:

  • springboot + shiro 權(quán)限注解、統(tǒng)一異常處理、請求亂碼解決 :https://blog.51cto.com/wyait/2125708

項目源碼:(包含數(shù)據(jù)庫源碼)

github源碼: https://github.com/wyait/manage.git
碼云:https://gitee.com/wyait/manage.git
github對應(yīng)項目源碼目錄:wyait-manage-1.2.0
碼云對應(yīng)項目源碼目錄:wyait-manage-1.2.0

動態(tài)更新用戶權(quán)限實現(xiàn)

使用shiro可能會遇到修改了用戶權(quán)限后,沒有立即生效,需要等到用戶重新登錄后才能生效;不能立即同步更新,顯然是不合理的。

解決方案

【實測無效】!?。?
授權(quán)方法,是在shiro進(jìn)行鑒權(quán)的時候才能觸發(fā)。只是配置了authc/user/anon等,不會觸發(fā);
perms,port,rest,roles,ssl等,會觸發(fā)授權(quán)方法doGetAuthorizationInfo。

  1. 自定義ShiroRealm中添加清除全部用戶權(quán)限緩存的方法:
/**
 * 清除所有緩存
 */
public void clearCachedAuth(){
    this.clearCachedAuthorizationInfo(SecurityUtils.getSubject().getPrincipals());
}
  1. 在更新用戶權(quán)限的地方調(diào)用該方法,清除緩存:
//清除ehcache中所有用戶權(quán)限緩存
RealmSecurityManager rsm = (RealmSecurityManager)SecurityUtils.getSecurityManager();
ShiroRealm authRealm = (ShiroRealm)rsm.getRealms().iterator().next();
authRealm.clearCachedAuth();
  1. 想要達(dá)到的效果:在用戶進(jìn)行后臺請求的時候,無緩存就會再次請求授權(quán)方法,更新自己的權(quán)限。
    實測,無效!緩存依然存在!原因可能是使用ehcache導(dǎo)致的。

實際解決方案參考下文中的方案二!

動態(tài)更新用戶信息(用戶資料、用戶權(quán)限)

在系統(tǒng)中,由管理員更改了用戶A信息后,如果用戶A在線,無法及時更新相關(guān)的改動;
更新用戶資料、權(quán)限等信息,如果該用戶在線,同步更新用戶信息解決方案:

方案一【不推薦】:SessionDAO控制

在ShiroRealm中通過SessionDAO拿到所有在線的用戶,

Collection<Session> sessions = sessionDAO.getActiveSessions();

遍歷找到匹配的,根據(jù)情況,退出登錄或更新用戶信息:

@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))) {
            //設(shè)置session立即失效,即將其踢出系統(tǒng)
            session.setTimeout(0);
            //TODO 或更新下用戶信息
            break;
        }   

    }

不推薦理由

  • 用戶數(shù)量太大的時候,效率問題。

方案二【推薦】:用戶表新增版本控制

用戶信息新增version版本標(biāo)記,寫個攔截器,每次請求判斷version是否一致,如有改動,根據(jù)情況,退出或更新用戶信息(本文統(tǒng)一做了退出登錄處理,可以結(jié)合實際需求做相應(yīng)調(diào)整)。

這個方案,基于樂觀鎖原理實現(xiàn)。同樣可解決動態(tài)更新用戶權(quán)限的問題。

user用戶表新增字段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【詳見源碼】

  • 更新對應(yīng)的pojo實體類和mapper文件
  • 完善用戶更新操作,版本控制;
新建攔截器類
  1. 自定義攔截器UserActionInterceptor實現(xiàn)接口HandlerInterceptor:
/**
 * 
 * @項目名稱:wyait-manage
 * @類名稱:UserActionInterceptor
 * @類描述:判斷用戶信息是否已被后臺更改,并根據(jù)更改的情況做對應(yīng)的處理
 * @創(chuàng)建人:wyait
 * @創(chuàng)建時間: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("請求到達(dá)后臺方法之前調(diào)用(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. 獲取數(shù)據(jù)庫中的用戶數(shù)據(jù)
            User dataUser = this.userService.findUserByMobile(user.getMobile());
            // 3. 對比session中用戶的version和數(shù)據(jù)庫中的是否一致
            if (dataUser != null
                    && null != dataUser.getVersion()
                    && String.valueOf(user.getVersion()).equals(
                            String.valueOf(dataUser.getVersion()))) {
                // 3.1 一樣,放行
                return true;
            }else{
                // 3.2 不一樣,這里統(tǒng)一做退出登錄處理;//TODO 使用redis緩存用戶權(quán)限數(shù)據(jù),根據(jù)用戶更新、用戶權(quán)限更新;做對應(yīng)的處理。
                SecurityUtils.getSubject().logout();
                isAjaxResponse(request,response);
            }
        }
        return false;
    }

    ... ...
}
  1. 自定義MyWebMvcConfig繼承WebMvcConfigurerAdapter
/**
 * 
 * @項目名稱:wyait-manage
 * @類名稱:MyWebMvcConfig
 * @類描述:自定義靜態(tài)資源映射路徑和靜態(tài)資源存放路徑
 * @創(chuàng)建人:wyait
 * @修改時間:2018年5月3日09:55:23
 * @version:
 */
@Configuration
public class MyWebMvcConfig extends WebMvcConfigurerAdapter {

    /**
     * 添加攔截器
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 路徑根據(jù)后期項目的擴展,進(jìn)行調(diào)整
        registry.addInterceptor(new UserActionInterceptor())
                .addPathPatterns("/user/**", "/auth/**")
                .excludePathPatterns("/user/sendMsg", "/user/login");
        super.addInterceptors(registry);
    }

}
  1. IStatusMessage接口和common.js中新增判斷‘1102’狀態(tài)碼;【詳見源碼】
  2. 啟動,測試
報錯

錯誤信息:

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;
  • 解決方案:
    在Spring添加攔截器之前先創(chuàng)建這個Spring Bean攔截器,這樣就能在Spring映射這個攔截器前,把攔截器中的依賴注入的對象給初始化完成了。避免攔截器中注入的對象為null問題。
@Configuration
public class MyWebMvcConfig extends WebMvcConfigurerAdapter {
    /**
     * 
     * @描述:在Spring添加攔截器之前先創(chuàng)建攔截器對象,這樣就能在Spring映射這個攔截器前,把攔截器中的依賴注入的對象給初始化完成了。
     * </br>避免攔截器中注入的對象為null問題。
     * @創(chuàng)建人:wyait
     * @創(chuàng)建時間:2018年5月3日 上午10:07:36
     * @return
     */
    @Bean
    public UserActionInterceptor userActionInterceptor(){
        return new UserActionInterceptor();
    }

    /**
     * 添加攔截器
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 路徑根據(jù)后期項目的擴展,進(jìn)行調(diào)整
        registry.addInterceptor(userActionInterceptor())
                .addPathPatterns("/user/**", "/auth/**")
                .excludePathPatterns("/user/sendMsg", "/user/login");
        super.addInterceptors(registry);
    }

}

注意

分布式或集群的時候,需要解決session共享問題;相關(guān)的方案有:session持久化、redis或其他中間件、nginx的ip_hash、cookie實現(xiàn)、服務(wù)器間Session同步等;這時候處理動態(tài)更新用戶信息,需要結(jié)合實際情況而定;

后期會更新redis版本。

jdk版本引發(fā)的錯誤

部署環(huán)境

linux 6.* 系統(tǒng)

jdk 1.7
tomcat 7.*
maven 3.3.3

線上tomcat啟動logs

五月 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啟動,會出現(xiàn)tomcat7和8版本沖突問題,本地解決了,線上依舊啟動不成功;再百度、google方案,大致是tomcat版本問題而打印的日志信息,按照搜索的方案配置,均未解決。

考慮應(yīng)該是環(huán)境配置的版本不一致導(dǎo)致的項目無法加載啟動成功,確認(rèn)并排查下開發(fā)、測試、線上的jdk、tomcat等版本是否一致。

原因及解決方案

  1. jdk版本
    確認(rèn)linux和window的jdk版本是否一致
    • windows查看jdk版本,dos窗口
java -version

jdk 1.8

  • linux系統(tǒng)查看jdk版本
java -v

jdk 1.7
這就是導(dǎo)致項目在linux系統(tǒng)啟動不起來的原因:開發(fā)和線上的jdk版本不一致?。?!

將windows的jdk版本切換為jdk1.7,重新打開新的dos窗口:java -version;jdk顯示為1.7.*。版本切換成功!

  1. maven重新打包,報錯:
...
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.
1:compile (default-compile) on project wyait-manage: Fatal error compiling: 無效的
目標(biāo)發(fā)行版: 1.8 -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e swit
ch.
...
  • 查看setting配置文件,找到<profile>配置:
<!--
<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版本的配置;重新打包,成功!

  1. 重新部署到linux系統(tǒng),啟動并訪問成功!
    問題解決。

總結(jié)

開發(fā)過程中,要確保開發(fā)、測試、線上配置的環(huán)境(jdk、maven、tomcat等開發(fā)依賴的環(huán)境支持)保持一致。避免出現(xiàn)由于開發(fā)環(huán)境中的版本不一致而出現(xiàn)問題,導(dǎo)致項目上線出問題和延遲項目上線時間!


前篇:

  • spring boot + mybatis + layui + shiro后臺權(quán)限管理系統(tǒng):https://blog.51cto.com/wyait/2082803
  • springboot + shiro之登錄人數(shù)限制、登錄判斷重定向、session時間設(shè)置:https://blog.51cto.com/wyait/2107423

后篇:

  • springboot + shiro 權(quán)限注解、統(tǒng)一異常處理、請求亂碼解決 :https://blog.51cto.com/wyait/2125708

項目源碼:(包含數(shù)據(jù)庫源碼)
github源碼: https://github.com/wyait/manage.git
碼云:https://gitee.com/wyait/manage.git
github對應(yīng)項目源碼目錄:wyait-manage-1.2.0
碼云對應(yīng)項目源碼目錄:wyait-manage-1.2.0

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI