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上的工作示例 。

参考: Java和社区博客上我们JCG合作伙伴 Tomasz Nurkiewicz提供的Spring Security应用程序中的su和sudo 。

翻译自: https://www.javacodegeeks.com/2013/07/su-and-sudo-in-spring-security-applications.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/367660.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

示例介绍:JavaFX 8打印

我有一段时间没有写博客了&#xff0c;我想与其他人分享有关JavaFX的所有信息&#xff08;我的日常工作和家庭可能是借口&#xff09;。 对于那些是本博客的新手&#xff0c;我是JavaFX 2 Introduction by Example&#xff08;JIBE&#xff09;的作者&#xff0c; Java 7 Recip…

placeholder的使用

1.定义 placeholder 属性提供可描述输入字段预期值的提示信息 该提示会在输入字段为空时显示&#xff0c;并会在字段获得焦点时消失。 注释&#xff1a;placeholder 属性适用于以下的 <input> 类型&#xff1a;text, search, url, telephone, email 以及 password。 2.用…

字符串练习

字符串练习&#xff1a; http://news.gzcc.cn/html/2017/xiaoyuanxinwen_1027/8443.html 取得校园新闻的编号 trhttp://news.gzcc.cn/html/2017/xiaoyuanxinwen_1027/8443.html print(a[-14:-5])https://docs.python.org/3/library/turtle.html 产生python文档的网址 trhttps:/…

CSS清除行内元素之间的HTML空白

至今我还记得年轻是在IE6上开发的那些苦逼日子,特别希望IE浏览器采用 inline-block 的显示方式.行内块(inline-block)是非常有用的,特别是想要不用block和float来控制这些行内元素的margin,padding之时。问题来了,HTML源码中行内元素之间的空白有时候显示在屏幕上那是相当的讨厌…

int64 java_为什么json 不能使用 int64类型

json 简介jsON(JavaScript Object Notation) 是一种轻量级的数据交换格式。 易于人阅读和编写。同时也易于机器解析和生成。 它基于JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999的一个子集 。 JSON采用完全独立于语言的文本格式&#xff0…

Spring MVC自定义验证注释

在上一教程中&#xff0c;我展示了如何使用注释来验证表单 。 这对于简单的验证非常有用&#xff0c;但是最终&#xff0c;您需要验证一些现成的注释中没有的自定义规则。 例如&#xff0c;如果您需要根据输入的出生日期来验证用户已超过21岁&#xff0c;或者可能需要验证用户的…

Best Time to Buy and Sell Stock with Cooldown

https://soulmachine.gitbooks.io/algorithm-essentials/java/dp/best-time-to-buy-and-sell-stock-with-cooldown.html转载于:https://www.cnblogs.com/ZhiHao-queue/p/9521933.html

前期

转载于:https://www.cnblogs.com/joker157/p/8618091.html

解决IE8下body{ overflow:hidden;}无效的解决办法

css中IE8 body{ overflow:hidden;}无效的解决办法&#xff1a; 在页面html中使用: body{ overflow:hidden; } 在ie8下无效 &#xff0c;仍然有滚动条。 解决的办法如下&#xff1a; 替换为如下: html { overflow:hidden; } 这样就可以实现隐藏滚动条了 而且兼容目前所有的浏览器…

0基础能学mysql数据库吗_mysql学习入门:零基础如何使用mysql创建数据库表?

零基础如何自学Mysql创建数据库&#xff0c;是Mysql学习者必经之路&#xff0c;Mysql是受欢迎的关系数据库管理系统,WEB应用方面MySQL是很好的RDBMS应用软件之一。如何使用Mysql创建数据库表&#xff0c;打开Mysql学习进阶大门&#xff0c;就是今天MYSQL学习教程丁光辉博客认为…

使用ANTLR和Java创建外部DSL

在以前的一段时间里&#xff0c;我曾写过有关使用Java的内部DSL的文章。 在Martin Fowler撰写的《 领域特定语言 》一书中&#xff0c;他讨论了另一种称为外部DSL的DSL&#xff0c;其中DSL是用另一种语言编写的&#xff0c;然后由宿主语言进行解析以填充语义模型。 在前面的示…

vue跨域解决及打包

打包之前需要修改如下配置文件&#xff1a; 配置文件一&#xff1a;build>>>utils.js (修改publicPath:"../../" , 这样写是处理打包后找不到静态文件&#xff08;图片路径失效&#xff09;的问题) 配置文件二&#xff1a;config>>>index.js(修改a…

8. Oracle 联机重做日志文件(ONLINE LOG FILE)

转载自&#xff1a;http://blog.csdn.net/leshami/article/details/5749556 一、Oracle中的几类日志文件 Redo log files -->联机重做日志 Archive log files -->归档日志 Alert log files -->告警日志 Trace files -->跟踪日志 user_dump_…

Bootstrap中实现图片圆角效果

Bootstrap 对图片的支持。Bootstrap 提供了三个可对图片应用简单样式的 class&#xff1a; .img-rounded&#xff1a;添加 border-radius:6px 来获得图片圆角。.img-circle&#xff1a;添加 border-radius:500px 来让整个图片变成圆形。.img-thumbnail&#xff1a;添加一些内边…

java 唯一索引冲突_JPA merge联合唯一索引无效问题解决方案

问题JPA的merge()操作 是合并的意思&#xff0c;就是当保存的实体时&#xff0c;根据主键id划分&#xff0c;如果已存在&#xff0c;那么就是更新操作&#xff0c;如果不存在&#xff0c;就是新增操作但是这个仅针对 主键id 划分&#xff0c;对联合唯一索引 无效&#xff0c;两…

Spring MVC测试框架入门–第1部分

最新推出的主要Spring框架是Spring MVC测试框架&#xff0c;Spring Guys声称它是“一流的JUnit支持&#xff0c;可通过流畅的API测试客户端和服务器端Spring MVC代码” 1 。 在这个博客以及下一个博客中&#xff0c;我将看一看Spring的MVC测试框架&#xff0c;并将其应用于我现…

metaclass

用metaclass来指定类C的元类是MyTypeclass MyType:def __init__(cls, *args, **kwargs):print(here!)#由于metaclassMyType&#xff0c;所以执行到这一步的时候&#xff0c;会调用MyType的构造函数 class C(metaclassMyType):def __init__(self):pass对象后面跟()&#xff0c;是…

Bootstrap中的条纹进度条使用案例

创建一个条纹的进度条的步骤如下&#xff1a;1.添加一个带有 class .progress 和 .progress-striped 的 <div>2.接着在上面的 <div> 内&#xff0c;添加一个带有 class .progress-bar 和 class progress-bar-* 的空的 <div>。其中&#xff0c;* 可以是succes…

LM拟合算法

一、 Levenberg-Marquardt算法 &#xff08;1&#xff09;ya*e.^(-b*x)形式拟合 clear all % 计算函数f的雅克比矩阵&#xff0c;是解析式 syms a b y x real; fa*exp(-b*x); Jsymjacobian(f,[a b]); % 拟合用数据。参见《数学试验》&#xff0c;p190&#xff0c;例2 % data_1…

java的前生今世_HBaseGC的前生今世-身世篇

网易视频云是网易倾力打造的一款基于云计算的分布式多媒体处理集群和专业音视频技术&#xff0c;提供稳定流畅、低时延、高并发的视频直播、录制、存储、转码及点播等音视频的PAAS服务&#xff0c;在线教育、远程医疗、娱乐秀网易视频云是网易倾力打造的一款基于云计算的分布式…