赣州建网站,机械设计网,施工企业主要负责人对安全生产的,专业团队简介很久以前#xff0c;我从事的项目具有很强大的功能。 有两个角色#xff1a;用户和主管。 主管可以以任何方式更改系统中的任何文档#xff0c;而用户则更受工作流约束的限制。 当普通用户对当前正在编辑和存储在HTTP会话中的文档有疑问时#xff0c;主管可以介入#xff… 很久以前我从事的项目具有很强大的功能。 有两个角色用户和主管。 主管可以以任何方式更改系统中的任何文档而用户则更受工作流约束的限制。 当普通用户对当前正在编辑和存储在HTTP会话中的文档有疑问时主管可以介入切换到特殊的主管模式并绕过所有约束。 完全自由。 相同的计算机相同的键盘相同的HTTP会话。 通过输入秘密密码只有管理员可以设置的特殊标志。 主管完成后他或她可以清除该标志并再次启用常规约束。 此功能运行良好但实施效果不佳。 每个单个输入字段的可用性取决于该超级用户模式标志。 使用isSupervisorMode()检查在数十个地方污染了业务方法。 请记住如果管理员只是使用常规凭据登录则此模式是隐式的因此安全约束基本上是重复的。 当我们的应用程序可以高度自定义并具有大量安全角色时就会出现另一个有趣的用例。 迟早您将面临异常确定 错误 您无法复制具有不同权限的异常。 能够以该特定用户身份登录并环顾四周可能是一个很大的胜利。 当然您不知道用户的密码 不是吗 。 类似UNIX的系统找到了解决此问题的方法 su 切换用户 和sudo命令。 出乎意料的是 Spring Security附带了内置的SwitchUserFilter 它在原则上模仿Web应用程序中的su 。 试一试吧 您需要声明自定义过滤器 bean idswitchUserProcessingFilterclassorg.springframework.security.web.authentication.switchuser.SwitchUserFilterproperty nameuserDetailsService refuserDetailsService/property nametargetUrl value//
/b:bean 并在http配置中指向它 http auto-configtrue use-expressionstruecustom-filter positionSWITCH_USER_FILTER refswitchUserProcessingFilter /intercept-url pattern/j_spring_security_switch_user accesshasRole(ROLE_SUPERVISOR)/... 而已 请注意我保护了/j_spring_security_switch_user URL模式。 您猜对了这就是您以其他用户身份登录的方式因此我们希望此资源得到良好的保护。 默认情况下使用j_username参数名称。 将上述更改应用于您的Web应用程序并以具有ROLE_SUPERVISOR的用户ROLE_SUPERVISOR登录后只需浏览以下内容即可 /j_spring_security_switch_user?j_usernamebob 假设存在这样的用户您将自动以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 ApplicationListenerAuthenticationSwitchUserEvent {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