将维护模式添加到Spring(安全)应用程序

时间:2015-03-02 16:33:30

标签: spring spring-mvc spring-security maintenance maintenance-mode

我正在寻找一种在Spring应用程序中实现维护模式的方法。

当应用处于维护模式时,只允许用户role = MAINTENANCE登录。其他人都会被重定向到登录页面。

现在我刚建了一个过滤器:

@Component
public class MaintenanceFilter extends GenericFilterBean {
    @Autowired SettingStore settings;

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        if(settingStore.get(MaintenanceMode.KEY).isEnabled()) {
            HttpServletResponse res = (HttpServletResponse) response; 
            res.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
        } else {
            chain.doFilter(request, response); 
        }
    }
}

并使用以下内容添加:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        // omitted other stuff
        .addFilterAfter(maintenanceFilter, SwitchUserFilter.class);
}

因为据我所知,SwitchUserFilter应该是Spring Security过滤器链中的最后一个过滤器。

现在每个请求都会被503响应取消。虽然无法访问登录页面。

如果我向Filter添加重定向,则会导致无限循环,因为对登录页面的访问也被拒绝。

此外,我无法找到获得当前用户角色的好方法。或者我应该选择SecurityContextHolder


我正在寻找一种方法将每个用户重定向到登录页面(可能带有查询参数?maintenance=true),每个role = MAINTENANCE用户都可以使用该应用程序。

所以Filter / Interceptor应该表现得像:

if(maintenance.isEnabled()) {
    if(currentUser.hasRole(MAINTENANCE)) {
        // this filter does nothing
    } else {
        redirectTo(loginPage?maintenance=true);
    }
}

2 个答案:

答案 0 :(得分:2)

我现在发现了两个类似的解决方案,但是我注入代码的地方并不好看。

对于这两者,我添加了一个自定义RequestMatcher,其中包含@Autowired并检查维护模式是否已启用。

解决方案1:

@Component
public class MaintenanceRequestMatcher implements RequestMatcher {
    @Autowired SettingStore settingStore;

    @Override
    public boolean matches(HttpServletRequest request) {
        return settingStore.get(MaintenanceMode.KEY).isEnabled()
    }
}

在我的安全配置中:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired MaintenanceRequestMatcher maintenanceRequestMatcher;

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .authorizeRequests()
            .antMatchers("/public/**").permitAll()
            .requestMatchers(maintenanceRequestMatcher).hasAuthority("MY_ROLE")
            .anyRequest().authenticated()
    // ...
}

解决方案2:

非常相似,但使用HttpServletRequest.isUserInRole(...)

@Component
public class MaintenanceRequestMatcher implements RequestMatcher {
    @Autowired SettingStore settingStore;

    @Override
    public boolean matches(HttpServletRequest request) {
        return settingStore.get(MaintenanceMode.KEY).isEnabled() && !request.isUserInRole("MY_ROLE");
    }
}

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired MaintenanceRequestMatcher maintenanceRequestMatcher;

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .authorizeRequests()
            .antMatchers("/public/**").permitAll()
            .requestMatchers(maintenanceRequestMatcher).denyAll()
            .anyRequest().authenticated()
    // ...
}

如果启用了维护模式并且当前用户没有denyAll(),则会执行MY_ROLE


唯一的缺点是,我无法设置自定义响应。我更愿意返回503 Service Unavailable。也许有人可以弄清楚如何做到这一点。

答案 1 :(得分:0)

它有点像鸡或蛋的困境,你想向未经授权的用户展示我们处于维护模式......"消息,虽然允许授权用户登录,但你不知道他们是否被授权,直到他们登录。理想情况下,在某种过滤器中使用它会很好,但我发现在实践中它对我来说更容易通过在登录后放置逻辑来解决类似问题,例如UserDetailsService

以下是我在项目中解决的问题。当我处于维护模式时,我为视图设置了一个标志,以显示"我们处于维护模式。"消息,全局标题或登录页面。所以用户,无论他们是谁都知道它的维护模式。登录应该正常工作。

在对用户进行身份验证之后,在我的自定义UserDetailsService中,用户的详细信息加载了他们的角色,我会执行以下操作:

// if we're in maintenance mode and does not have correct role
if(maintenance.isEnabled() && !loadedUser.hasRole(MAINTENANCE)) {
  throw new UnauthorizedException(..)
}
// else continue as normal

它并不漂亮,但它很容易理解(我认为它对安全配置有好处)并且它有效。

更新

  

有了你的解决方案,我必须摧毁每个人的会话,否则就是一个用户   在启用维护模式之前登录,仍然可以   使用系统

在我们的项目中,我们不允许任何用户在维护模式下登录。管理员启动一项任务,启用"维护..." msg,倒数,然后在最后,我们使用SessionRegistry使所有人的会话失效。