spring security中動態更新用戶的權限

業務背景: 管理員更改其他用戶權限,正好用戶在登陸狀態下,無法刷新管理員剛賦值的權限,只能退出登錄,重新登錄才能擁有新權限.
業務需求:管理員更改權限,其他用戶不退出登錄,可以擁有新權限,動態刷新session
感謝這位博客提供的線索
前提條件需要擁有

@Autowired
    SessionRegistry sessionRegistry;

如果找不到SessionRegistrybean 需要自行註冊

/**
     * 註冊bean sessionRegistry
     */
    @Bean
    public SessionRegistry sessionRegistry() {
        return new SessionRegistryImpl();
    }

配置security http

http
		... 省略代碼
				.and()
                .sessionManagement()
                // 無效session跳轉
                .invalidSessionUrl(login)
                .maximumSessions(1)
                // session過期跳轉
                .expiredUrl(login)
                .sessionRegistry(sessionRegistry())
       ... 省略代碼         

定義一個 UserSessionManage

package com.gszcn.hisweb.config.session;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpSession;
import java.util.*;

/**
 * 用戶存儲session
 * 一個用戶儲存多個session 出現一個用戶多個地方登錄
 */
@Component
@Data
@Slf4j
@CacheConfig(cacheNames = "UserSessionManage")
public class UserSessionManage {
    /**
     * 用戶存儲session對象
     */
    private Map<String,List<HttpSession>> userHttpSession=new HashMap <>();

    /**
     * 獲取當前在線的session
     * @param id
     * @return
     */
    @Cacheable(key = "#id")
    public List<HttpSession> getUserSession(String id) {
        List <HttpSession> httpSessions = userHttpSession.get(id);
        if(httpSessions==null){
            httpSessions=new ArrayList <>();
        }
        return httpSessions;
    }

    /**
     * 修改緩存
     * @param id
     * @param userSession
     */
    @CachePut(key = "#id")
    public List<HttpSession> putUserSession(String id, List<HttpSession> userSession) {
        userHttpSession.put(id,userSession);
        return userSession;
    }
}

然後在定義一個SessionManage

package com.gszcn.hisweb.config.session;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * session管理容器
 */
@Data
@Slf4j
@Component
@CacheConfig(cacheNames = "SessionManage")
public class SessionManage {
    @Autowired
    UserSessionManage userSessionManage;
    /**
     * 創建map容器
     */
    private Map<String,HttpSession> httpSessionMap=new HashMap <>();

    /**
     * 創建session
     * @param id
     * @param session
     */
    @Cacheable(key = "#id")
    public HttpSession createdSession(String id, HttpSession session) {
        httpSessionMap.put(id,session);
        return session;
    }

    /**
     * 銷燬session
     * @param id
     */
    @CacheEvict(key = "#id")
    public void destroyedSession(String id) {
        httpSessionMap.remove(id);
    }

    /**
     * 改變session
     * @param oldSessionId
     * @param newSessionId
     * @return
     */
    public void changeSession(String oldSessionId, String newSessionId) {
        //查詢舊的session

    }
    /**
     * 根據sessionid查詢session
     */
    @Cacheable(key = "#id")
    public HttpSession getSessionID(String id){
        return httpSessionMap.get(id);
    }

    /**
     * 當前用戶保存session
     * @param id 用戶id
     * @param httpSessions
     */
    @Cacheable(key = "'user'+#id",unless = "!#result.contains(#httpSessions)")
    public List <HttpSession> putUserSession(String id, HttpSession httpSessions) {
        List <HttpSession> userSession = userSessionManage.getUserSession(id);
        if(!userSession.contains(httpSessions)){
            userSession.add(httpSessions);
        }
        userSessionManage.putUserSession(id,userSession);
        return userSession;
    }
}

監聽每次的請求防止多賬號不同步權限

 /**
     * 監聽Servlet 請求
     */
    @Component
    class ServletRequestHandledEventListener implements ApplicationListener <ServletRequestHandledEvent> {

        @Override
        public void onApplicationEvent(ServletRequestHandledEvent event) {
            log.info("ServletRequestHandledEventListener:{}",event);
            String sessionId = event.getSessionId();
            SessionInformation sessionInformation = sessionRegistry.getSessionInformation(sessionId);
            if(sessionInformation==null){return;}
            Object principal = sessionInformation.getPrincipal();


            try {
                if(principal instanceof SysUser){
                    SysUser user = (SysUser) principal;
                    sessionManage.putUserSession(user.getId(),SecurityUtil.getHttpSession());
                }

            } catch (Exception e) {
                log.warn("當前無法監聽到用戶信息");
            }

        }
    }

sessionManage 中取出當前在線用戶的所有session

/**
     * 刷新session
     * 刷新在線用戶角色
     * @param save
     */
    private void refreshSession(SysUser save) {
        UserSessionManage userSessionManage = sessionManage.getUserSessionManage();
        List<HttpSession> userSession = userSessionManage.getUserSession(save.getId());
        userSession.forEach(session -> {
            this.reloadUserAuthority(session);
        });
    }

當修改角色調用刷新session中的在線用戶權限

/**
     * 重新加載用戶的權限
     *
     * @param session
     */
    public void reloadUserAuthority(HttpSession session) {
        try {
            SecurityContext securityContext = (SecurityContext) session.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
            Authentication authentication = securityContext.getAuthentication();
            Object principal1 = authentication.getPrincipal();
            if (principal1 instanceof SysUser) {
                UserDetails principal = (SysUser) principal1;
                /**
                 * 重載用戶對象
                 */
                principal = this.loadUserByUsername(principal.getUsername());

                // 重新new一個token,因爲Authentication中的權限是不可變的.
                UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(
                        principal, authentication.getCredentials(),
                        principal.getAuthorities());
                result.setDetails(authentication.getDetails());
                securityContext.setAuthentication(result);
            }
        } catch (Exception e) {
            log.error("當前用戶session有可能不存在");
        }

    }

本頁面的代碼本人測試無誤,請放心參考,當然源碼不提供公司的代碼,如果又不懂的地方請留言或者聯繫QQ:1810258114及時幫你解答

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章