博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
SwitchUserFilter源码解析
阅读量:5746 次
发布时间:2019-06-18

本文共 13835 字,大约阅读时间需要 46 分钟。

本文就来解析一下SwitchUserFilter的源码

SwitchUserFilter

spring-security-web-4.2.3.RELEASE-sources.jar!/org/springframework/security/web/authentication/switchuser/SwitchUserFilter.java

public class SwitchUserFilter extends GenericFilterBean        implements ApplicationEventPublisherAware, MessageSourceAware {    //......    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)            throws IOException, ServletException {        HttpServletRequest request = (HttpServletRequest) req;        HttpServletResponse response = (HttpServletResponse) res;        // check for switch or exit request        if (requiresSwitchUser(request)) {            // if set, attempt switch and store original            try {                Authentication targetUser = attemptSwitchUser(request);                // update the current context to the new target user                SecurityContextHolder.getContext().setAuthentication(targetUser);                // redirect to target url                this.successHandler.onAuthenticationSuccess(request, response,                        targetUser);            }            catch (AuthenticationException e) {                this.logger.debug("Switch User failed", e);                this.failureHandler.onAuthenticationFailure(request, response, e);            }            return;        }        else if (requiresExitUser(request)) {            // get the original authentication object (if exists)            Authentication originalUser = attemptExitUser(request);            // update the current context back to the original user            SecurityContextHolder.getContext().setAuthentication(originalUser);            // redirect to target url            this.successHandler.onAuthenticationSuccess(request, response, originalUser);            return;        }        chain.doFilter(request, response);    }}
首先会判断url是不是/login/impersonate或者/logout/impersonate,如果不是则不会进入这个filter

attemptSwitchUser

/**     * Attempt to switch to another user. If the user does not exist or is not active,     * return null.     *     * @return The new Authentication request if successfully switched to     * another user, null otherwise.     *     * @throws UsernameNotFoundException If the target user is not found.     * @throws LockedException if the account is locked.     * @throws DisabledException If the target user is disabled.     * @throws AccountExpiredException If the target user account is expired.     * @throws CredentialsExpiredException If the target user credentials are expired.     */    protected Authentication attemptSwitchUser(HttpServletRequest request)            throws AuthenticationException {        UsernamePasswordAuthenticationToken targetUserRequest;        String username = request.getParameter(this.usernameParameter);        if (username == null) {            username = "";        }        if (this.logger.isDebugEnabled()) {            this.logger.debug("Attempt to switch to user [" + username + "]");        }        UserDetails targetUser = this.userDetailsService.loadUserByUsername(username);        this.userDetailsChecker.check(targetUser);        // OK, create the switch user token        targetUserRequest = createSwitchUserToken(request, targetUser);        if (this.logger.isDebugEnabled()) {            this.logger.debug("Switch User Token [" + targetUserRequest + "]");        }        // publish event        if (this.eventPublisher != null) {            this.eventPublisher.publishEvent(new AuthenticationSwitchUserEvent(                    SecurityContextHolder.getContext().getAuthentication(), targetUser));        }        return targetUserRequest;    }
从url读取username参数,然后调用userDetailsService.loadUserByUsername(username)获取目标用户信息,然后判断目标账户是否正常,正常则切换,不正常则抛异常

AccountStatusUserDetailsChecker

spring-security-core-4.2.3.RELEASE-sources.jar!/org/springframework/security/authentication/AccountStatusUserDetailsChecker.java

public class AccountStatusUserDetailsChecker implements UserDetailsChecker {    protected final MessageSourceAccessor messages = SpringSecurityMessageSource            .getAccessor();    public void check(UserDetails user) {        if (!user.isAccountNonLocked()) {            throw new LockedException(messages.getMessage(                    "AccountStatusUserDetailsChecker.locked", "User account is locked"));        }        if (!user.isEnabled()) {            throw new DisabledException(messages.getMessage(                    "AccountStatusUserDetailsChecker.disabled", "User is disabled"));        }        if (!user.isAccountNonExpired()) {            throw new AccountExpiredException(                    messages.getMessage("AccountStatusUserDetailsChecker.expired",                            "User account has expired"));        }        if (!user.isCredentialsNonExpired()) {            throw new CredentialsExpiredException(messages.getMessage(                    "AccountStatusUserDetailsChecker.credentialsExpired",                    "User credentials have expired"));        }    }}

createSwitchUserToken

/**     * Create a switch user token that contains an additional GrantedAuthority     * that contains the original Authentication object.     *     * @param request The http servlet request.     * @param targetUser The target user     *     * @return The authentication token     *     * @see SwitchUserGrantedAuthority     */    private UsernamePasswordAuthenticationToken createSwitchUserToken(            HttpServletRequest request, UserDetails targetUser) {        UsernamePasswordAuthenticationToken targetUserRequest;        // grant an additional authority that contains the original Authentication object        // which will be used to 'exit' from the current switched user.        Authentication currentAuth;        try {            // SEC-1763. Check first if we are already switched.            currentAuth = attemptExitUser(request);        }        catch (AuthenticationCredentialsNotFoundException e) {            currentAuth = SecurityContextHolder.getContext().getAuthentication();        }        GrantedAuthority switchAuthority = new SwitchUserGrantedAuthority(                this.switchAuthorityRole, currentAuth);        // get the original authorities        Collection
orig = targetUser.getAuthorities(); // Allow subclasses to change the authorities to be granted if (this.switchUserAuthorityChanger != null) { orig = this.switchUserAuthorityChanger.modifyGrantedAuthorities(targetUser, currentAuth, orig); } // add the new switch user authority List
newAuths = new ArrayList
(orig); newAuths.add(switchAuthority); // create the new authentication token targetUserRequest = new UsernamePasswordAuthenticationToken(targetUser, targetUser.getPassword(), newAuths); // set details targetUserRequest .setDetails(this.authenticationDetailsSource.buildDetails(request)); return targetUserRequest; }
找出目标账号,添加SwitchUserGrantedAuthority,然后创建UsernamePasswordAuthenticationToken

attemptExitUser

/**     * Attempt to exit from an already switched user.     *     * @param request The http servlet request     *     * @return The original Authentication object or null     * otherwise.     *     * @throws AuthenticationCredentialsNotFoundException If no     * Authentication associated with this request.     */    protected Authentication attemptExitUser(HttpServletRequest request)            throws AuthenticationCredentialsNotFoundException {        // need to check to see if the current user has a SwitchUserGrantedAuthority        Authentication current = SecurityContextHolder.getContext().getAuthentication();        if (null == current) {            throw new AuthenticationCredentialsNotFoundException(                    this.messages.getMessage("SwitchUserFilter.noCurrentUser",                            "No current user associated with this request"));        }        // check to see if the current user did actual switch to another user        // if so, get the original source user so we can switch back        Authentication original = getSourceAuthentication(current);        if (original == null) {            this.logger.debug("Could not find original user Authentication object!");            throw new AuthenticationCredentialsNotFoundException(                    this.messages.getMessage("SwitchUserFilter.noOriginalAuthentication",                            "Could not find original Authentication object"));        }        // get the source user details        UserDetails originalUser = null;        Object obj = original.getPrincipal();        if ((obj != null) && obj instanceof UserDetails) {            originalUser = (UserDetails) obj;        }        // publish event        if (this.eventPublisher != null) {            this.eventPublisher.publishEvent(                    new AuthenticationSwitchUserEvent(current, originalUser));        }        return original;    }
这个方法无论是登录切换,还是注销切换都需要调用。登录切换会调动这个方法判断是否已经切换过了.

getSourceAuthentication

/**     * Find the original Authentication object from the current user's     * granted authorities. A successfully switched user should have a     * SwitchUserGrantedAuthority that contains the original source user     * Authentication object.     *     * @param current The current Authentication object     *     * @return The source user Authentication object or null     * otherwise.     */    private Authentication getSourceAuthentication(Authentication current) {        Authentication original = null;        // iterate over granted authorities and find the 'switch user' authority        Collection
authorities = current.getAuthorities(); for (GrantedAuthority auth : authorities) { // check for switch user type of authority if (auth instanceof SwitchUserGrantedAuthority) { original = ((SwitchUserGrantedAuthority) auth).getSource(); this.logger.debug("Found original switch user granted authority [" + original + "]"); } } return original; }
这个方法会检查,当前账号是否具有SwitchUserGrantedAuthority,如果有则找出切换前的账号。
对于登录切换,通过这个方法判断是否已经切换过(
如果你调用这个方法自己切换自己,则这里会抛出AuthenticationCredentialsNotFoundException异常,createSwitchUserToken会捕获这个异常,然后将登录态切换成当前的登录态;不过比没切换之前多了个SwitchUserGrantedAuthority)。
而对于注销切换,则通过这个找出切换前的身份,如果找不到则抛出AuthenticationCredentialsNotFoundException,但是外层没有捕获
if (requiresExitUser(request)) {            // get the original authentication object (if exists)            Authentication originalUser = attemptExitUser(request);            // update the current context back to the original user            SecurityContextHolder.getContext().setAuthentication(originalUser);            // redirect to target url            this.successHandler.onAuthenticationSuccess(request, response, originalUser);            return;        }
因而会返回错误页面

SwitchUserGrantedAuthority

spring-security-web-4.2.3.RELEASE-sources.jar!/org/springframework/security/web/authentication/switchuser/SwitchUserGrantedAuthority.java

/** * Custom {@code GrantedAuthority} used by * {@link org.springframework.security.web.authentication.switchuser.SwitchUserFilter} * 

* Stores the {@code Authentication} object of the original user to be used later when * 'exiting' from a user switch. * * @author Mark St.Godard * * @see org.springframework.security.web.authentication.switchuser.SwitchUserFilter */public final class SwitchUserGrantedAuthority implements GrantedAuthority { private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; // ~ Instance fields // ================================================================================================ private final String role; private final Authentication source; // ~ Constructors // =================================================================================================== public SwitchUserGrantedAuthority(String role, Authentication source) { this.role = role; this.source = source; } // ~ Methods // ======================================================================================================== /** * Returns the original user associated with a successful user switch. * * @return The original Authentication object of the switched user. */ public Authentication getSource() { return source; } public String getAuthority() { return role; } public int hashCode() { return 31 ^ source.hashCode() ^ role.hashCode(); } public boolean equals(Object obj) { if (this == obj) { return true; } if (obj instanceof SwitchUserGrantedAuthority) { SwitchUserGrantedAuthority swa = (SwitchUserGrantedAuthority) obj; return this.role.equals(swa.role) && this.source.equals(swa.source); } return false; } public String toString() { return "Switch User Authority [" + role + "," + source + "]"; }}

这个保存了账户切换的关联关系

小结

  • 切换权限判断

这个通过security config里头配置,在FilterSecurityInterceptor里头进行鉴权

  • 账号关联

通过SwitchUserGrantedAuthority来保存切换之前的账号信息

  • 状态切换(登录切换/注销切换)

获取目标用户的UsernamePasswordAuthenticationToken,之后调用

// update the current context to the new target user                SecurityContextHolder.getContext().setAuthentication(targetUser);                // redirect to target url                this.successHandler.onAuthenticationSuccess(request, response,                        targetUser);
这两个方法一个再上下文切换登录态,一个是调用登录成功之后的处理。这里没有改变sessionId。但是如果是正常登陆的话,会切换sessionId的。

登录切换是通过userDetailsService.loadUserByUsername(username)获取目标用户信息,然后创建UsernamePasswordAuthenticationToken;

注销切换则是通过SwitchUserGrantedAuthority获取原账号的UsernamePasswordAuthenticationToken

转载地址:http://rvazx.baihongyu.com/

你可能感兴趣的文章
OracleLinux安装说明
查看>>
nova分析(7)—— nova-scheduler
查看>>
Entity Framework 实体框架的形成之旅--Code First模式中使用 Fluent API 配置(6)
查看>>
OpenMediaVault 搭建git,ssh无法连接问题
查看>>
java多线程之:Java中的ReentrantLock和synchronized两种锁定机制的对比 (转载)
查看>>
mysql性能优化学习笔记-参数介绍及优化建议
查看>>
【Web动画】SVG 实现复杂线条动画
查看>>
使用Wireshark捕捉USB通信数据
查看>>
《树莓派渗透测试实战》——1.1 购买树莓派
查看>>
Apache Storm 官方文档 —— FAQ
查看>>
iOS 高性能异构滚动视图构建方案 —— LazyScrollView
查看>>
Java 重载、重写、构造函数详解
查看>>
【Best Practice】基于阿里云数加·StreamCompute快速构建网站日志实时分析大屏
查看>>
【云栖大会】探索商业升级之路
查看>>
HybridDB实例新购指南
查看>>
C语言及程序设计提高例程-35 使用指针操作二维数组
查看>>
华大基因BGI Online的云计算实践
查看>>
深入理解自定义Annotation,实现ButterKnif小原理
查看>>
排序高级之交换排序_冒泡排序
查看>>
Cocos2d-x3.2 Ease加速度
查看>>