spring如何实现权限实时生效,如何获取在线用户,getAllPrincipals()为空

一、问题:

1、spring如何实现权限实时生效

spring security的权限修改后无法实时更新,由于使用的spring security做权限控制,并且在用户登录时,将该时刻用户的权限(authority)读到用户的UserDetails(org.springframework.security.core.userdetails.UserDetails)类中,如果用户不主动重新登录,那么这个写入到session中的类无法刷新。

我自己给出一个权限实时更新的解决办法:
写一个拦截器,拦截器中有一个static的map用于存储哪些用户需要去更新自己的权限,然后在更新权限的接口中往这个static的map喂数据。
模拟使用场景:用户A、B已经登录,用户A修改用户B的权限后,会将用户B加入到static的map中,只要当前的请求的session在map中那么就更新自己的session中的权限。

@Component
public class PermissionFlushInterceptor implements HandlerInterceptor {
    /**
     * key is the sessionId of the logged in user, value is the username of
     * the session.
     * when the user's permission is updated, add the username to this map
     */
    private static ConcurrentHashMap<String, String>
            usersNeedUpdatePermissions = new ConcurrentHashMap();

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {
        String sessionId = request.getSession().getId();
        Authentication auth =
                SecurityContextHolder.getContext().getAuthentication();
        if (auth != null) {
            if (auth.getPrincipal() instanceof AuthenticationUser) {
                AuthenticationUser userDetails = (AuthenticationUser) auth.getPrincipal();
                if (usersNeedUpdatePermissions.size() > 0 &&
                    usersNeedUpdatePermissions.containsKey(sessionId)) {
                    updatePermissionInSession(auth, userDetails);
                    usersNeedUpdatePermissions.remove(sessionId);
                }
            }
        }
        return true;
    }

    private void updatePermissionInSession(Authentication auth,
                                           AuthenticationUser authenticationUser) {
        String username = authenticationUser.getUsername();
        AuthenticationUser newAuthUser =
                new AuthenticationUser.Builder(username)
                        .authorities(AgentContextImpl.getInstance()
                                                     .getRoleManager()
                                                     .getUserAllPermissions(
                                                             username))
                        .build();
        HttpAuthenticationUtils.setAuthentication(newAuthUser);
    }

    public static ConcurrentHashMap<String, String> getUsersNeedUpdatePermissions() {
        return usersNeedUpdatePermissions;
    }
}

拦截器需要加到WebMvcConfigurer中,这样前端的每次请求都会走这个拦截器。

  • 这里为什么key是sessionId?
    因为目前的登录策略设置的是允许同一个用户多浏览器同时登录,所以他们的区别是sessionId不相同,所以精确到sessionId作为key,不使用username,引入sessionId才能够从map中进行remove操作,username区分不了不同的浏览器。

  • AuthenticationUser 是实现了org.springframework.security.core.userdetails.UserDetails接口的自定义的用户信息类。需要重写hashCode和equals方法,重写好像是为了让spring知道只要用户名相同就是同一个对象。


public class AuthenticationUser implements UserDetails {

    private String username;
    private Set<AuthorityInfo> authorities = new HashSet<>();
		
	@Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        AuthenticationUser that = (AuthenticationUser) o;
        return Objects.equals(username, that.username);
    }

    @Override
    public int hashCode() {
        return Objects.hash(username);
    }

	// other code ...
}

在修改用户角色,和修改角色权限的位置获取到当前系统在登录的用户,将在登录的用户加入在map中,(因为只需要这一部分在登录用户刷新自己的权限就行了)。

2、如何获取在线用户

需要获取当前spring服务的登录用户,哪些用户在登录状态,一个稍微好的博客写的解决办法
我大致思路和这个类似,也是用

sessionRegistry.getAllPrincipals()

理想的获取方式是:

 @Autowired
 @Qualifier("sessionRegistry")
 private SessionRegistry sessionRegistry;
    
 public Map<String, Integer> getAllLoggedInUsers() {
        List<Object> allPrincipals = sessionRegistry.getAllPrincipals();
        // key is the username, value is the session count of the username.
        Map<String, Integer> usernames = new HashMap<>();
        for (Object principal : allPrincipals) {
            if (principal instanceof AuthenticationUser) {
                AuthenticationUser authenticationUser =
                        (AuthenticationUser) principal;
                List<SessionInformation> allSessions =
                        sessionRegistry.getAllSessions(authenticationUser,
                                                       false);
                usernames.put(authenticationUser.getUsername(),
                              allSessions.size());
            }
        }
        return usernames;
    }

spring boot的配置类:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class AgentSecurityConfig extends WebSecurityConfigurerAdapter {


    @Value("${server.host.url}")
    private String serverHostUrl;

    /**
     * Security strategy
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/v1/**").authenticated()
            .anyRequest().permitAll()
            .and()
            .formLogin().disable()
            .and()
            .csrf().disable();

        http.sessionManagement()
            .sessionAuthenticationStrategy(concurrentSession());

        http.addFilterBefore(
                new CustomConcurrentSessionFilter(sessionRegistry()),
                ConcurrentSessionFilter.class);
    }

    @Bean
    public SessionRegistry sessionRegistry() {
        return new SessionRegistryImpl();
    }

    @Bean
    public static ServletListenerRegistrationBean httpSessionEventPublisher() {
        return new ServletListenerRegistrationBean(new HttpSessionEventPublisher());
    }

    @Bean
    public CompositeSessionAuthenticationStrategy concurrentSession() {
        ConcurrentSessionControlAuthenticationStrategy
                concurrentAuthenticationStrategy =
                new ConcurrentSessionControlAuthenticationStrategy(
                        sessionRegistry());
        List<SessionAuthenticationStrategy> delegateStrategies =
                new ArrayList<SessionAuthenticationStrategy>();
        delegateStrategies.add(concurrentAuthenticationStrategy);
        delegateStrategies.add(new SessionFixationProtectionStrategy());
        delegateStrategies.add(
                new RegisterSessionAuthenticationStrategy(sessionRegistry()));
        // concurrentAuthenticationStrategy还可以进行一些配置,此处设置的是允许同一个用户多个浏览器同时登录
        concurrentAuthenticationStrategy.setMaximumSessions(-1);
        return new CompositeSessionAuthenticationStrategy(delegateStrategies);
    }
}

在登录成功的先引入策略类:

	// 先引入seesion管理的策略类,
    @Autowired
    @Qualifier("concurrentSession")
    private SessionAuthenticationStrategy sessionAuthenticationStrategy;

再调用session策略类的onAuthentication方法,表示认证成功,这一过程就经过了设置的策略,这样能够避免sessionRegistry.getAllPrincipals()为空的情况。

AuthenticationUser authUser =
                new AuthenticationUser.Builder(username)
                        .authorities(AgentContextImpl.getInstance()
                                                     .getRoleManager()
                                                     .getUserAllPermissions(username))
                        .build();
UsernamePasswordAuthenticationToken authentication =
                new UsernamePasswordAuthenticationToken(
                        authUser, null, authUser.getAuthorities());
        sessionAuthenticationStrategy.onAuthentication(authentication, request, response);

另一种方式获取在线用户,思想很简单,(未验证)

import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

@WebListener
public class SessionListener implements HttpSessionListener{
    private int onlineCount = 0;//记录session的数量
    /**
     * session创建后执行
     */
    @Override
    public void sessionCreated(HttpSessionEvent se) {
        onlineCount++;
        System.out.println("【HttpSessionListener监听器】 sessionCreated, onlineCount:" + onlineCount);
       //将最新的onlineCount值存起来
        se.getSession().getServletContext().setAttribute("onlineCount", onlineCount);
    }
    /**
     * session失效后执行
     */
    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        if (onlineCount > 0) {
            onlineCount--;
        }
        System.out.println("【HttpSessionListener监听器】 sessionDestroyed, onlineCount:" + onlineCount);
        //将最新的onlineCount值存起来
        se.getSession().getServletContext().setAttribute("onlineCount", onlineCount);
    }
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值