将维护模式添加到 Spring(安全)应用程序
Posted
技术标签:
【中文标题】将维护模式添加到 Spring(安全)应用程序【英文标题】:Add Maintenance Mode to Spring (Security) app 【发布时间】:2015-05-03 02:27:06 【问题描述】:我正在寻找一种在我的 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 响应。虽然无法访问登录页面。
如果我向过滤器添加重定向,这将导致无限循环,因为对登录页面的访问也会被拒绝。
此外,我想不出一种获取当前用户角色的好方法。还是我应该选择SecurityContextHolder
?
我正在寻找一种将每个用户重定向到登录页面的方法(可能使用查询参数?maintenance=true
)并且每个使用role = MAINTENANCE
的用户都可以使用该应用程序。
所以过滤器/拦截器的行为应该像:
if(maintenance.isEnabled())
if(currentUser.hasRole(MAINTENANCE))
// this filter does nothing
else
redirectTo(loginPage?maintenance=true);
【问题讨论】:
【参考方案1】:我现在找到了两个类似的解决方案,但我注入代码的地方看起来不太好。
我添加了一个自定义的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()
// ...
如果启用维护模式并且当前用户没有MY_ROLE
,这将执行denyAll()
。
唯一的缺点是,我无法设置自定义响应。我更愿意返回503 Service Unavailable
。也许有人可以弄清楚如何做到这一点。
【讨论】:
感谢您的回答。这是一个工作示例github.com/ozkanpakdil/spring-examples/tree/master/…【参考方案2】:这有点像先有鸡还是先有蛋的两难境地,您想向未经授权的用户显示“我们处于维护模式...”消息,同时允许授权用户登录,但您不知道他们是否被授权,直到他们登录。理想情况下,最好在某种过滤器中使用它,但我发现在实践中通过在登录后放置逻辑更容易解决类似的问题,就像在 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
它并不漂亮,但它很容易理解(我认为这对安全配置很有好处)并且它有效。
更新:
有了你的解决方案,我必须销毁每个人的会话,否则用户 在启用维护模式之前登录的,仍然可以 与系统合作
在我们的项目中,我们不允许任何用户在维护模式下登录。管理员启动启用“维护...”消息的任务,倒计时,然后在最后,我们使用SessionRegistry
使每个人的会话到期。
【讨论】:
有了你的解决方案,我必须销毁每个人的会话,否则在启用维护模式之前登录的用户仍然能够使用系统。...
我只是想知道 Spring 的登录拦截器是如何工作的。这正是工作:如果用户在调用资源时没有经过身份验证,那么他会被重定向到登录页面。这有点像我想要/需要的。【参考方案3】:
我也遇到过类似的情况,发现这个答案很有帮助。我遵循了第二种方法,并且还设法返回了自定义响应。
这是我为返回自定义响应所做的工作。
1- 定义返回所需自定义响应的控制器方法。
@RestController
public class CustomAccessDeniedController
@GetMapping("/access-denied")
public String getAccessDeniedResponse()
return "my-access-denied-page";
2- 在您的安全上下文中,您应该允许此 URL 可访问。
http.authorizeRequests().antMatchers("/access-denied").permitAll()
3- 创建自定义访问被拒绝异常处理程序
@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler
@Autowired
private SettingStore settingStore;
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException
if(settingStore.get(MaintenanceMode.KEY).isEnabled())
response.sendRedirect(request.getContextPath() + "/access-denied");
4- 在安全配置中注册自定义访问拒绝异常处理程序
@Autowired
private CustomAccessDeniedHandler accessDeniedHandler;
http.exceptionHandling().accessDeniedHandler(accessDeniedHandler);
【讨论】:
以上是关于将维护模式添加到 Spring(安全)应用程序的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Symfony 2 中进入维护模式以安全地更新生产应用程序?
text 如果用户未登录,则仅显示维护模式。将maintenance.php文件添加到wp-content文件夹以自定义维护页面。