Spring Security应用程序中的su和sudo
很久以前,我从事的项目具有很强大的功能。 有两个角色:用户和主管。 主管可以以任何方式更改系统中的任何文档,而用户则更受工作流约束的限制。 当普通用户对当前正在编辑和存储在HTTP会话中的文档有疑问时,主管可以介入,切换到特殊的主管模式并绕过所有约束。 完全自由。 相同的计算机,相同的键盘,相同的HTTP会话。 通过输入秘密密码,只有管理员可以设置的特殊标志。 主管完成后,他或她可以清除该标志并再次启用常规约束。
此功能运行良好,但实施效果不佳。 每个单个输入字段的可用性取决于该超级用户模式标志。 使用isSupervisorMode()
检查在数十个地方污染了业务方法。 请记住,如果管理员只是使用常规凭据登录,则此模式是隐式的,因此安全约束基本上是重复的。
当我们的应用程序可以高度自定义并具有大量安全角色时,就会出现另一个有趣的用例。 迟早您将面临异常(确定, 错误 ),您无法复制具有不同权限的异常。 能够以该特定用户身份登录并环顾四周可能是一个很大的胜利。 当然,您不知道用户的密码( 不是吗? )。 类似UNIX的系统找到了解决此问题的方法: su
( 切换用户 )和sudo
命令。 出乎意料的是, Spring Security附带了内置的SwitchUserFilter
,它在原则上模仿Web应用程序中的su
。 试一试吧!
您需要声明自定义过滤器:
<bean id="switchUserProcessingFilter"class="org.springframework.security.web.authentication.switchuser.SwitchUserFilter"><property name="userDetailsService" ref="userDetailsService"/><property name="targetUrl" value="/"/>
</b:bean>
并在<http>
配置中指向它:
<http auto-config="true" use-expressions="true"><custom-filter position="SWITCH_USER_FILTER" ref="switchUserProcessingFilter" /><intercept-url pattern="/j_spring_security_switch_user" access="hasRole('ROLE_SUPERVISOR')"/>...
而已! 请注意,我保护了/j_spring_security_switch_user
URL模式。 您猜对了,这就是您以其他用户身份登录的方式,因此我们希望此资源得到良好的保护。 默认情况下,使用j_username
参数名称。 将上述更改应用于您的Web应用程序并以具有ROLE_SUPERVISOR
的用户ROLE_SUPERVISOR
登录后,只需浏览以下内容即可:
/j_spring_security_switch_user?j_username=bob
假设存在这样的用户,您将自动以bob
身份登录。 此处不需要密码。 模拟完他后,浏览至/j_spring_security_exit_user
将还原您以前的凭据。 当然,所有这些URL是可配置的。 参考文档中未记录SwitchUserFilter
,但谨慎使用时它是非常有用的工具。
确实具有强大的力量…… 。 像任何其他任意用户一样,甚至让最受信任的用户登录也具有很大的风险。 想象一下在Facebook上的这种功能,这是不可能的! ( 很好…… )因此,跟踪和审核成为一项主要要求。
我通常首先要做的是在Spring Security过滤器之后添加一个小的servlet过滤器,该过滤器将用户名添加到MDC :
import org.slf4j.MDC;public class UserNameFilter implements Filter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();final String userName = authentication.getName();final String fullName = userName + (realName != null ? " (" + realName + ")" : "");MDC.put("user", fullName);try {chain.doFilter(request, response);} finally {MDC.remove("user");}}private String findSwitchedUser(Authentication authentication) {for (GrantedAuthority auth : authentication.getAuthorities()) {if (auth instanceof SwitchUserGrantedAuthority) {return ((SwitchUserGrantedAuthority)auth).getSource().getName();}}return null;}//...
}
只要记住在 Spring Security 之后将其添加到web.xml
。 此时,您可以在logback.xml
引用"user"
键:
<pattern>%d{HH:mm:ss.SSS} | %-5level | %X{user} | %thread | %logger{1} | %m%n%rEx</pattern>
看到%X{user}
代码段? 每次登录的用户在系统中执行某些触发日志语句的操作时,都会看到该用户的名称:
21:56:55.074 | DEBUG | alice | http-bio-8080-exec-9 | ...
//...
21:56:57.314 | DEBUG | bob (alice) | http-bio-8080-exec-3 | ...
第二个日志语句很有趣。 如果您查看上面的findSwitchedUser()
调用,很明显,作为管理员的alice
切换到用户bob
,现在代表他浏览。
有时您需要更强大的审核系统。 幸运的是,Spring框架具有内置的事件基础结构,我们可以利用当有人切换用户并退出此模式时发送的AuthenticationSwitchUserEvent
:
@Service
public class SwitchUserListenerimplements ApplicationListener<AuthenticationSwitchUserEvent> {private static final Logger log = LoggerFactory.getLogger(SwitchUserListener.class);@Overridepublic void onApplicationEvent(AuthenticationSwitchUserEvent event) {log.info("User switch from {} to {}",event.getAuthentication().getName(),event.getTargetUser().getUsername());}
}
当然,您可以使用所需的任何业务逻辑来替换简单的日志记录,例如,将此类事件存储在数据库中或向安全员发送电子邮件。
因此,我们知道如何以其他用户身份登录一段时间,然后退出这种模式。 但是,如果我们需要“ sudo
”,即代表其他用户仅发出一个HTTP请求,该怎么办? 当然,我们可以切换到该用户,运行该请求,然后退出。 但这似乎太繁重且麻烦。 当客户端程序访问我们的API并希望以其他用户身份查看数据时(考虑测试复杂的ACL),可能会弹出这样的要求。
添加自定义HTTP标头以表示这样的特殊模拟请求听起来很合理。 假设客户端已经在进行身份验证(例如使用JSESSIONID cookie),则该功能仅在一个请求期间有效。 不幸的是,Spring Security不支持此功能,但是很容易在SwitchUserFilter
之上SwitchUserFilter
:
public class SwitchUserOnceFilter extends SwitchUserFilter {@Overridepublic void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest) req;final String switchUserHeader = request.getHeader("X-Switch-User-Once");if (switchUserHeader != null) {trySwitchingUserForThisRequest(chain, request, res, switchUserHeader);} else {super.doFilter(req, res, chain);}}private void trySwitchingUserForThisRequest(FilterChain chain, HttpServletRequest request, ServletResponse response, String switchUserHeader) throws IOException, ServletException {try {proceedWithSwitchedUser(chain, request, response, switchUserHeader);} catch (AuthenticationException e) {throw Throwables.propagate(e);}}private void proceedWithSwitchedUser(FilterChain chain, HttpServletRequest request, ServletResponse response, String switchUserHeader) throws IOException, ServletException {final Authentication targetUser = attemptSwitchUser(new SwitchUserRequest(request, switchUserHeader));SecurityContextHolder.getContext().setAuthentication(targetUser);try {chain.doFilter(request, response);} finally {final Authentication originalUser = attemptExitUser(request);SecurityContextHolder.getContext().setAuthentication(originalUser);}}}
与原始SwitchUserFilter
的唯一区别是,如果存在"X-Switch-User-Once"
,则将凭据切换到由此标头的值表示的用户-但是仅在一个HTTP请求期间。 SwitchUserFilter
假定要切换到的用户名在j_username
参数下,因此我不得不使用SwitchUserRequest
包装器作弊:
private class SwitchUserRequest extends HttpServletRequestWrapper {private final String switchUserHeader;public SwitchUserRequest(HttpServletRequest request, String switchUserHeader) {super(request);this.switchUserHeader = switchUserHeader;}@Overridepublic String getParameter(String name) {switch (name) {case SPRING_SECURITY_SWITCH_USERNAME_KEY:return switchUserHeader;default:return super.getParameter(name);}}
}
我们的自定义“ sudo
”就位了! 您可以使用curl
进行测试:
$ curl localhost:8080/books/rest/book \-H "X-Switch-User-Once: bob" \-b "JSESSIONID=..."
当然,如果没有JSESSIONID
cookie,系统将不允许我们进入。我们必须首先登录,并具有访问sudo
功能的特殊特权。 切换用户是一个方便且功能强大的工具。 如果您想在实践中尝试,请查看GitHub上的工作示例 。
翻译自: https://www.javacodegeeks.com/2013/07/su-and-sudo-in-spring-security-applications.html
Spring Security应用程序中的su和sudo相关推荐
- sudo su su_Spring Security应用程序中的su和sudo
sudo su su 很久以前,我从事的项目具有很强大的功能. 有两个角色:用户和主管. 主管可以以任何方式更改系统中的任何文档,而用户则更受工作流约束的限制. 当普通用户对当前正在编辑并存储在HTT ...
- Spring MVC,Thymeleaf,Spring Security应用程序中的CSRF保护
跨站点请求伪造(CSRF)是一种攻击,它迫使最终用户在当前已通过身份验证的Web应用程序上执行不需要的操作. 如果您使用Spring Security 3.2及更高版本,在Spring MVC / T ...
- kafka 启动_「首席看Event Hub」如何在您的Spring启动应用程序中使用Kafka
在体系结构规划期间选择正确的消息传递系统始终是一个挑战,但这是需要确定的最重要的考虑因素之一.作为一名开发人员,我每天都要编写需要服务大量用户并实时处理大量数据的应用程序. 通常,我将Java与Spr ...
- 如何在Spring Boot应用程序中使用配置文件
你好朋友, 在本教程中,我们将学习如何在Spring Boot应用程序中使用配置文件. 我们将在本教程中讨论以下几点: 1.什么是Spring Boot Profile,为什么我们需要分析 2.如何使 ...
- 在使用Gradle构建的Spring Boot应用程序中覆盖Spring Framework版本
如果要使用或仅通过Spring Boot检查Spring的最新版本,但当前的Spring Boot版本取决于旧的Spring版本,则需要稍微调整Gradle构建配置. 例如,在撰写本文时,Spring ...
- 在Spring Boot应用程序中测试邮件代码
在构建Spring Boot应用程序时,您可能会需要添加邮件配置. 实际上,在Spring Boot中配置邮件与在Spring Bootless应用程序中配置邮件没有太大区别. 但是,如何测试邮件配置 ...
- 在Spring MVC应用程序中使用Bean Validation 1.1获得更好的错误消息
在许多新功能中, Bean Validation 1.1引入了使用统一表达式语言(EL)表达式的错误消息插值. 这允许基于条件逻辑来定义错误消息,还可以启用高级格式化选项 . 添加到Spring MV ...
- Spring MVC应用程序中的Thymeleaf模板布局,无扩展
在使用JSP / JSTL和Apache Tiles几年之后,我开始为我的Spring MVC应用程序发现Thymeleaf. Thymeleaf是一个非常出色的视图引擎,尽管目前缺乏良好的Intel ...
- java ldap操作实例_Java Spring Security示例教程中的2种设置LDAP Active Directory身份验证的方法...
java ldap操作实例 LDAP身份验证是世界上最流行的企业应用程序身份验证机制之一,而Active Directory (Microsoft为Windows提供的LDAP实现)是另一种广泛使用的 ...
最新文章
- 系统设计面试题思路综述
- MongoDB基本命令总结
- POJ - 3436 ACM Computer Factory(最大流+输出残余网络)
- gRPC in ASP.NET Core 3.0 -- 前言
- 【百度地图API】如何制作一张魔兽地图!!——CS地图也可以,哈哈哈
- jquery输入框按下回车提交表单
- cpu怎么超频_小白秒变高手 Intel酷睿CPU一键超频就是这么简单
- 打开CAD图纸转换成dwf格式的文件
- STM32驱动WS2811
- java 前后端分离
- Java解决高并发下商品库存更新
- 谷歌学术搜索 简易PDF爬虫
- 解除百度云下载限制速度(谷歌浏览器)
- 5 月计算机语言排行,TIOBE 5月编程语言排行榜
- Markdown 插入图片技巧
- 算法竞赛进阶指南0x10练习7:Corral the Cows
- 【论文】AMC:AutoML用于移动设备上的模型压缩和加速
- Java中的isnan函数_Java Float类isNaN()方法与示例
- 消息服务MNS和消息队列ONS产品对比
- Python大数据分析与应用—2020年中国高校毕业生薪酬指数排名
热门文章
- python参数_python参数的介绍
- java 8 Lambda 表达式(副作用)
- (转)ThreadPoolExecutor最佳实践--如何选择队列
- 牛客网JAVA专项联系共899题--个人记录学习经历
- 空调吸气和排气_吸气剂和二传手被认为有害
- java使用缓冲区读取文件_在Java中使用Google的协议缓冲区
- java 指令重拍_我发现我的Java重拍了!
- Java面试准备:15个Java面试问题
- activemq主从配置_使用ActiveMQ –具有故障转移协议的“主/从”配置
- 具有ELK的APIGEE API网关日志管理(Elastic Search,Logstash和Kibana)