浅析基于Spring Security 的身份认证流程

标签: spring security 身份认证 | 发表时间:2023-03-21 08:09 | 作者:政采云技术团队
出处:https://juejin.cn/backend

政采云技术团队.png

海洋.png

引言

在企业级项目中,目前较为流行的的认证和授权框架主要是 Spring Security 和 Shiro。Spring Security 相对于 Shiro 具有更加丰富的功能和社区资源,但相对而言 Spring Security 的上手难度也要大于 Shiro。因此,一般中大型项目会更倾向于使用 Spring Security 框架,而小型项目多采用 Shiro 框架。

1 Spring Security 是什么?官方是这样介绍的。

“Spring Security is a framework that focuses on providing both authentication and authorization to Java applications. Like all Spring projects, the real power of Spring Security is found in how easily it can be extended to meet custom requirements.”

Spring Security 是一个专注于为 Java 应用程序提供身份认证和授权的框架。这句话简明扼要地说出了 Spring Security 的两个核心功能:

  • 身份认证(authentication),即验证用户身份的合法性,以判断用户能否登录。
  • 授权(authentication),即验证用户是否有权限访问某些资源或者执行某些操作。

本文将结合源码对身份认证(authentication)的流程和核心组件进行详细讲解。

Spring Security 进行身份认证这一功能主要是通过一系列的过滤器链配合来实现的。在我们启动 Spring Security 项目时可以在控制台看到 DefaultSecurityFilterChain 打印出的默认过滤器链。

  public DefaultSecurityFilterChain(RequestMatcher requestMatcher, List<Filter> filters) { 
    logger.info("Creating filter chain: " + requestMatcher + ", " + filters); 
    this.requestMatcher = requestMatcher; this.filters = new ArrayList(filters); 
}

1.jpg

1.1 Spring Security 核心过滤器

先简要说一下过滤器(Filter),Filter 可以在服务器作出响应前拦截用户请求,并在拦截后修改 request 和 response,可实现一次编码、多处应用。Filter 主要有以下两点作用:

  1. 拦截请求:在 HttpServletRequest 到达 Servlet 之前进行拦截,查看和修改 HttpServletRequest 的 Header 和数据。
  2. 拦截响应:在 HttpServletResponse 到达客户端之前完成拦截,查看和修改 HttpServletResponse 的 Header 和数据。

过滤器链作为 Spring Security 的核心,下图呈现 Spring Security 过滤器链的一个简要执行流程。本文将以该流程为基础对认证流程进行剖析:

2.jpg

Spring Security 过滤器链执行流程图

  • SecurityContextPersistenceFilter:整个 Spring Security 过滤器链的开端。主要有两点作用:(1)当请求到来时,检查 Session中是否存在 SecurityContext,若不存在,则创建一个新的 SecurityContext;(2)在请求结束时,将 SecurityContext 放入 Session 中,并清空 SecurityContextHolder。
  • UsernamePasswordAuthenticationFilter:继承自抽象类 AbstractAuthenticationProcessingFilter。当进行表单登录时,该 Filter 将用户名和密码封装成 UsernamePasswordAuthenticationToken 进行验证。
  • AnonymousAuthenticationFilter:匿名身份过滤器,一般用于匿名登录。当前面的 Filter 认证后依然没有用户信息时,该Filter会生成一个匿名身份 AnonymousAuthenticationToken。
  • ExceptionTranslationFilter:异常转换过滤器,用于处理 FilterSecurityInterceptor 抛出的异常。但是只会处理两类异常:AuthenticationException 和 AccessDeniedException,其它的异常它会继续抛出。

1.2 Spring Security 核心组件

  1. SecurityContextHolder:用于获取 SecurityContext 的静态工具类,是Spring Security 存储 身份验证者详细信息的位置。
  2. SecurityContext: 上下文对象,Authentication 对象会放在里面。
  3. Authentication: 认证接口,定义了认证对象的数据形式。
  4. AuthenticationManager: 用于校验 Authentication,返回一个认证完成后的 Authentication 对象。

1、SecurityContextHolder

  public class SecurityContextHolder {
​
    public static void clearContext() {
        strategy.clearContext();
    }
    public static SecurityContext getContext() {
        return strategy.getContext();
    }
    public static void setContext(SecurityContext context) {
        strategy.setContext(context);
    }
}

SecurityContextHolder 用于存储安全上下文(SecurityContext)的信息。而如何保证用户信息的安全,Spring Security 采用“用户信息和线程绑定”的策略,SecurityContextHolder 默认采用 ThreadLocal 机制保存用户的 SecurityContext,在使用中可以通过 SecurityContextHolder 工具轻松获取用户安全上下文。这意味着,只要是针对某个使用者的逻辑执行都是在同一个线程中进行,Spring Security 会在用户登录时自动绑定认证信息到当前线程,在用户退出时也会自动清除当前线程的认证信息。

111-第 5 页.drawio.png

SecurityContextHolder、SecurityContext 和 Authentication 的关系如图中所示。

  Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); 
SecurityUserDetails userDetails = (SecurityUserDetails) authentication.getPrincipal();

其中,getAuthentication() 返回认证信息,getPrincipal() 返回身份信息。 SecurityContext 是从 SecurityContextHolder 获得的。SecurityContext 包含一个 Authentication对象。

当然,有些应用程序并不完全适合使用 ThreadLocal 模式。例如,有些应用希望能在子线程中获取登录用户的信息,亦或者希望所有线程使用相同的安全上下文。 针对这些情况,SecurityContextHolder 可以在启动时配置策略以指定您希望如何存储上下文。

  • MODE_THREADLOCAL:SecurityContext 存储在线程中。
  • MODE_INHERITABLETHREADLOCAL:SecurityContext 存储在线程中,但子线程可以获取到父线程中的 SecurityContext。
  • MODE_GLOBAL:SecurityContext 在所有线程中都相同。

2、SecurityContext

  public interface SecurityContext extends Serializable { 
    Authentication getAuthentication(); 
    
    void setAuthentication(Authentication var1); 
}

SecurityContext 即安全上下文,其中存储当前操作的用户是谁、该用户是否已经被认证、该用户拥有哪些角色权限等信息。

3、Authentication

  public interface Authentication extends Principal, Serializable {
    Collection<? extends GrantedAuthority> getAuthorities();
​
    Object getCredentials();
​
    Object getDetails();
​
    Object getPrincipal();
​
    boolean isAuthenticated();
​
    void setAuthenticated(boolean var1) throws IllegalArgumentException;
}
  • getAuthorities( ):获取权限信息列表。
  • getCredentials( ):获取密码信息,用户输入的密码字符串,在认证过后通常会被移除,保障用户信息安全。
  • getDetails( ):获取细节信息,web 应用中的实现接口通常为 WebAuthenticationDetails,它记录了访问者的 ip 地址和 sessionId 的值。
  • getPrincipal( ):获取身份信息,大部分情况下返回 UserDetails 接口的实现类,是框架中的常用接口之一。
  • isAuthenticated( ):判断认证状态,默认是 false,认证成功为 true。
  • setAuthenticated( ):设置是否进行认证。

4、AuthenticationManager

  public interface AuthenticationManager {
  Authentication authenticate(Authentication var1) throws AuthenticationException;
}

AuthenticationManager 接口其中只有一个方法 authenticate(),该方法用于进行身份验证,验证通过则返回带有完整认证身份信息的Authentication(包括 Authentication 中的所有属性)。

2 Spring Security认证流程

111-Page-4.drawio.png 结合上面的时序图,让我们先熟悉下 Spring Security 的认证流程:

  1. 用户进行认证,用户名和密码被 SecurityFilterChain 中的 UsernamePasswordAuthenticationFilter 过滤器拦截,并将请求封装为 Authentication,其默认实现类是 UsernamePasswordAuthenticationToken。
  2. 将封装的 UsernamePasswordAuthenticationToken 提交至 AuthenticationManager(认证管理器)进行认证。
  3. 认证成功后, AuthenticationManager(身份管理器)会返回一个包含用户身份信息的 Authentication 实例(包括身份信息,细节信息,但密码通常会被移除)。
  4. SecurityContextHolder (安全上下文容器)将认证成功的 Authentication 存储到 SecurityContext(安全上下文)中。

其中,AuthenticationManager 接口是认证相关的核心接口,ProviderManager 是它的实现类。因为 Spring Security 支持多种认证方式,所以 ProviderManager 维护着一个List<AuthenticationProvider> 列表,包含多种认证方式,最终实际的认证工作就是由列表中的 AuthenticationProvider 完成的。其中最常见的 web 表单认证的对应的 AuthenticationProvider 实现类为 DaoAuthenticationProvider,它的内部又维护着一个 UserDetailsService负责获取 UserDetails。最终 AuthenticationProvider 将 UserDetails 填充至 Authentication。

3 Spring Security认证的底层实现流程

1-2.png

根据 Spring Security 的认证流程图和源码,接下来一起看下 Spring Security 如何完成用户名密码认证。

步骤1:当用户发起认证请求,AbstractAuthenticationProcessingFilter 从 HttpServletRequest 创建 Authentication进行身份验证。创建的身份验证类型取决于 AbstractAuthenticationProcessingFilter 的子类。

3.1 UsernamePasswordAuthenticationFilter

以用户名密码认证为例 ,请求被 UsernamePasswordAuthenticationFilter 过滤器拦截,UsernamePasswordAuthenticationFilter 根据Request 中提交的用户名和密码创建一个 Token(UsernamePasswordAuthenticationToken)。

  public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
    public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
    public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
    private String usernameParameter = "username";
    private String passwordParameter = "password";
    private boolean postOnly = true;
​
    public UsernamePasswordAuthenticationFilter() {
      //匹配url = "/login", method ="POST" 的请求
        super(new AntPathRequestMatcher("/login", "POST"));
    }
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        if (this.postOnly && !request.getMethod().equals("POST")) {
      //method 不是"POST" 抛异常
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        } else {
      //获取请求参数:用户名、密码
            String username = this.obtainUsername(request);
            String password = this.obtainPassword(request);
            if (username == null) {
                username = "";
            }
​
            if (password == null) {
                password = "";
            }
​
            username = username.trim();
        //先根据请求的用户名和密码构造一个未认证的Token
            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
      //将请求信息存储到刚刚创建的UsernamePasswordAuthenticationToken中,其中包括ip、session等信息。
            this.setDetails(request, authRequest);
      //核心:将token交给AuthenticationManager处理。
            return this.getAuthenticationManager().authenticate(authRequest);
        }
    }
  protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {
        authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
    }
}

3.2 UsernamePasswordAuthenticationToken

UsernamePasswordAuthenticationToken 又是什么呢?

  public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationToken {
    private static final long serialVersionUID = 500L;
    private final Object principal;
    private Object credentials;
  
  //构造方法,用于初始化一个未认证的Token
    public UsernamePasswordAuthenticationToken(Object principal, Object credentials) {
        super((Collection)null);
        this.principal = principal;
        this.credentials = credentials;
        this.setAuthenticated(false);
    }
  //构造方法,用于初始化一个已认证的Token
    public UsernamePasswordAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
        super(authorities);
        this.principal = principal;
        this.credentials = credentials;
        super.setAuthenticated(true);
    }
​
    public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
        //注意:如果直接调用该方法设置认证状态未已认证会直接抛出异常,异常的提示很明确,无法将此令牌设置为受信任 - 使用采用 GrantedAuthority 列表的构造函数
    if (isAuthenticated) {
            throw new IllegalArgumentException("Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
        } else {
            super.setAuthenticated(false);
        }
    }
}

从上面的源码可以看出,其实 UsernamePasswordAuthenticationToken 的核心就是两个构造方法,分别用于初始化未认证和认证的 Token。

步骤2:将生成的未认证 Token 交给 AuthenticationManager 进行身份认证。 1-3.png

这一步是身份认证的核心,下面进行详细讲解:

  1. 未认证的 UsernamePasswordAuthenticationToken(携带用户名、密码信息)被提交给 AuthenticationManager。AuthenticationManager 的实现类 ProviderManager 负责对认证请求链 AuthenticationProviders 进行管理。
  2. ProviderManager 通过循环的方式,发现 DaoAuthenticationProvider 的类型符合,使用 DaoAuthenticationProvider 进行认证。
  3. DaoAuthenticationProvider 从 UserDetailsService 中查找 UserDetails。
  4. DaoAuthenticationProvider 使用 PasswordEncoder 验证上一步返回的 UserDetails 中的用户密码。
  5. 当身份验证成功, Authentication 返回一个已认证的 UsernamePasswordAuthenticationToken ,其中包含 UserDetailsService 返回的 UserDetails 信息。最终,认证成功的 UsernamePasswordAuthenticationToken 添加到 SecurityContextHolder 完成账号密码的身份认证。

3.3 AuthenticationManager

AuthenticationManager 作为一个最上级的接口本身不包含验证的逻辑,只有一个 authenticate 方法用于处理身份验证请求,返回一个 Authentication 对象。它的作用只是用来管理 AuthenticationProvider。

  public interface AuthenticationManager {
    Authentication authenticate(Authentication var1) throws AuthenticationException;
}

AuthenticationManager 的实现方式有很多,通常是使用 ProviderManager 对认证请求链进行管理; 这里可以看一下 ProviderManager 源码中 authenticate 方法的注解。

  * The list of {@link AuthenticationProvider}s will be successively tried until an <code>AuthenticationProvider</code> indicates it is capable of authenticating the type of <code>Authentication</code> object passed. 
Authentication will then be attempted with that <code>AuthenticationProvider</code>.  
* If more than one <code>AuthenticationProvider</code> supports the passed <code>Authentication</code> object, the first one able to successfully authenticate the <code>Authentication</code> object determines the <code>result</code>,  overriding any possible <code>AuthenticationException</code> thrown by earlier supporting <code>AuthenticationProvider</code>s.
On successful authentication, no subsequent <code>AuthenticationProvider</code>s will be tried.
* If authentication was not successful by any supporting <code>AuthenticationProvider</code> the last thrown <code>AuthenticationException</code> will be rethrown.

翻译如下: AuthenticationProvider 列表将被链式连续尝试执行,直到一个 AuthenticationProvider 能够验证通过,即可结束。 如果任何一个能够成功验证 Authentication 对象确定结果,就会忽略之前 AuthenticationProvider 认证失败抛出的异常以及 null 响应。成功认证后,不会尝试后续的 AuthenticationProviders。 如果任何支持 AuthenticationProvider 的身份验证未成功,则最后抛出的 AuthenticationException 将被重新抛出。

简单点说, List 中只要有一个 AuthenticationProvider 能够验证成功,不管之前是否有失败的情况,结果都会返回这次成功的结果,之前抛出的异常也会被忽略。

3.4 ProviderManager

这里看一下ProviderManage r的源码,它是通过 for 循环的方式获取 AuthenticationProvider 对象。

  public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    Class<? extends Authentication> toTest = authentication.getClass();
    AuthenticationException lastException = null;
    AuthenticationException parentException = null;
    Authentication result = null;
    Authentication parentResult = null;
    int currentPosition = 0;
    int size = this.providers.size();
    //这里循环遍历AuthenticationProvider
    for (AuthenticationProvider provider : getProviders()) {
      // 判断是否支持处理
      if (!provider.supports(toTest)) {
        continue;
      }
      if (logger.isTraceEnabled()) {
        logger.trace(LogMessage.format("Authenticating request with %s (%d/%d)",
            provider.getClass().getSimpleName(), ++currentPosition, size));
      }
      try {
        //调用实现类的authenticate方法进行真实业务逻辑认证处理
        result = provider.authenticate(authentication);
        if (result != null) {
          copyDetails(authentication, result);
          break;
        }
      }
      catch (AccountStatusException | InternalAuthenticationServiceException ex) {
        prepareException(ex, authentication);
        throw ex;
      }
      catch (AuthenticationException ex) {
        lastException = ex;
      }
    }
  }

authenticate 这个方法是在 ProviderManager 类上的,这个类实现了 AuthenticationManager 接口,通过 for 循环的方式去获取所有的 AuthenticationProvider,而真正校验的逻辑是写在 AuthenticationProvider 中的。AuthenticationManager 其实只是将 AuthenticationProvider 收集起来,然后在登录时逐个进入 AuthenticationProvider 并判断此种验证逻辑是否支持本次登录方式,根据传进来的 Authentication 类型挑选出符合的 provider 进行校验处理。

3.5 AuthenticationProvider

  public interface AuthenticationProvider{
      Authentication authenticate(Authentication authentication) throws AuthenticationException;
​
      boolean supports(Class<?> authentication);
}

Spring Security 进行身份认证的方式有许多种,除了最常见的账号密码认证、手机号认证,还有 ReremberMe 认证、三方认证等,它们均遵守着由 AuthenticationProvider 来处理认证请求的规则。

可以说到目前为止,还没有进行”真正的“账号密码校验。有些地方说是在默认的 DaoAuthenticationProvider 中判断 Authentication 类型进行判断直接完成的。其实并不是很准确,应该说 AuthenticationProvider 的认证其实由 AbstractUserDetailsAuthenticationProvider 抽象类和 AbstractUserDetailsAuthenticationProvider 的子类 DaoAuthenticationProvider共同协助完成的。

3.6 AbstractUserDetailsAuthenticationProvider

   public abstract class AbstractUserDetailsAuthenticationProvider
        implements AuthenticationProvider, InitializingBean, MessageSourceAware {
 
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        //断言入参Authentication如果不是UsernamePasswordAuthenticationToken类型,直接抛异常。
        Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
                () -> this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports",
                        "Only UsernamePasswordAuthenticationToken is supported"));
        //拿到用户名
        String username = determineUsername(authentication);
        boolean cacheWasUsed = true;
        //通过用户名从缓存中获取UserDetails
        UserDetails user = this.userCache.getUserFromCache(username);
        //若缓存中没有,从子类DaoAuthenticationProvider中获取
        if (user == null) {
            cacheWasUsed = false;
            try {
                // 子类DaoAuthenticationProvider实现
                user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
            }
            catch (UsernameNotFoundException ex) {
                this.logger.debug("Failed to find user '" + username + "'");
                if (!this.hideUserNotFoundExceptions) {
                    throw ex;
                }
                throw new BadCredentialsException(this.messages
                        .getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
            }
            Assert.notNull(user, "retrieveUser returned null - a violation of the interface contract");
        }
        try {
            //前置检查:检查当前用户是否锁定、过期或冻结
            this.preAuthenticationChecks.check(user);
            additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);
        }
        catch (AuthenticationException ex) {
            if (!cacheWasUsed) {
                throw ex;
            }
            cacheWasUsed = false;
            user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
            this.preAuthenticationChecks.check(user);
            additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);
        }
        //后置检查:检查用户的凭据/密码是否过期
        this.postAuthenticationChecks.check(user);
        if (!cacheWasUsed) {
        //如果用户信息没有被缓存,放入缓存
            this.userCache.putUserInCache(user);
        }
        Object principalToReturn = user;
        if (this.forcePrincipalAsString) {
            principalToReturn = user.getUsername();
        }
        //返回通过验证的用户信息,封装成了UsernamePasswordAuthenticationToken
        return createSuccessAuthentication(principalToReturn, authentication, user);
    }

通过上面的源码,和标注的注释。可以清晰的了解其验证流程:

  1. 首先获取用户名 username;然后尝试从缓存中获取 User;若缓存中没有,再调用 retrieveUser 方获取( 子类DaoAuthenticationProvider 实现)。
  2. 然后,进行前置身份认证检查( 校验账号是否锁定、是否可用、是否过期)和额外的检查( 凭据/密码校验)。进行后置身份认证检查( 校验凭据/密码是否过期
  3. 若当前用户还没有被缓存的话,将用户存入缓存;最后,创建身份认证成功的 Authentication。

3.7 DaoAuthenticationProvider

DaoAuthenticationProvider AuthenticationProvider 最常用的实现,其作用在注释中已经清楚的表明。

  * An {@link AuthenticationProvider} implementation that retrieves user details from a
* {@link UserDetailsService}.

即通过 UsernamePasswordAuthenticationToken 获取到 username,然后通过 UserDetailsService 获取用户详细信息。

下面看一下 DaoAuthenticationProvider 是如何重写的 retrieveUser 和 additionalAuthenticationChecks 两个方法。(部分源码省略)

  protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
    this.prepareTimingAttackProtection();
    try {
       //通过UserDetailsService获取UserDetails信息。(从自定义的UserDetailsService获取)
        UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
        if (loadedUser == null) {
            throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");
        } else {
            return loadedUser;
            }
    }
}
protected void additionalAuthenticationChecks(UserDetails userDetails,
      UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
   if (authentication.getCredentials() == null) {
      this.logger.debug("Failed to authenticate since no credentials provided");
      throw new BadCredentialsException(this.messages
            .getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
   }
   String presentedPassword = authentication.getCredentials().toString();
    //验证密码是否正确
   if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
      this.logger.debug("Failed to authenticate since password does not match stored value");
      throw new BadCredentialsException(this.messages
            .getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
   }
}

可以看出,主要是利用 UserDetailsServicePasswordEncoder 来实现获取用户信息和密码认证。

至此身份认证的源码分析就结束了,在 spring security 的整个认证流程中其实最难理解的就是 AuthenticationManager 、ProviderManager、AuthenticationProvider 之间的关系,其关系如下图所示。

7.jpg 不同的认证方式对应不同的 AuthenticationProvider,一个完整的认证流程可由多个 AuthenticationProvider 来达成。在实际使用中,很多特定场景需要我们自定义 AuthenticationProvider 来进行认证。

步骤3:认证成功。

将认证信息存储到 SecurityContextHodler 并调用 RememberMeServices(如有开启)等,回调 AuthenticationSuccessHandler 进行处理。

步骤4:认证失败。

将认证信息存储到 SecurityContextHodler 并调用 RememberMeServices(如有开启)等,并回调 AuthenticationSuccessHandler 处理。

核心组件的调用流程:

下图即总结的核心组件的调用流程。在实际运用或问题排查过程中,我们均可以参照下面的流程进行学习和排查。 spring security filter-第 4 页.drawio (1).png

4 总结

本文仅针对 Spring Security 的认证流程进行了详细讲解,但Spring Security 登录相关的内容远不止这些。如果大家有更好的想法,欢迎多多交流,一起学习,一起进步!

5 参考文档

推荐阅读

MySQL - InnoDB 内存结构解析

浅谈AI目标检测技术发展史

数据仓库模型重构实践

从源码看Lucene的两阶段提交

Kubernetes弹性扩缩容之HPA和KEDA

招贤纳士

政采云技术团队(Zero),一个富有激情、创造力和执行力的团队,Base 在风景如画的杭州。团队现有 500 多名研发小伙伴,既有来自阿里、华为、网易的“老”兵,也有来自浙大、中科大、杭电等校的新人。团队在日常业务开发之外,还分别在云原生、区块链、人工智能、低代码平台、中间件、大数据、物料体系、工程平台、性能体验、可视化等领域进行技术探索和实践,推动并落地了一系列的内部技术产品,持续探索技术的新边界。此外,团队还纷纷投身社区建设,目前已经是 google flutter、scikit-learn、Apache Dubbo、Apache Rocketmq、Apache Pulsar、CNCF Dapr、Apache DolphinScheduler、alibaba Seata 等众多优秀开源社区的贡献者。如果你想改变一直被事折腾,希望开始折腾事;如果你想改变一直被告诫需要多些想法,却无从破局;如果你想改变你有能力去做成那个结果,却不需要你;如果你想改变你想做成的事需要一个团队去支撑,但没你带人的位置;如果你想改变本来悟性不错,但总是有那一层窗户纸的模糊……如果你相信相信的力量,相信平凡人能成就非凡事,相信能遇到更好的自己。如果你希望参与到随着业务腾飞的过程,亲手推动一个有着深入的业务理解、完善的技术体系、技术创造价值、影响力外溢的技术团队的成长过程,我觉得我们该聊聊。任何时间,等着你写点什么,发给  [email protected]

微信公众号

文章同步发布,政采云技术团队公众号,欢迎关注

政采云技术团队.png

相关 [spring security 身份认证] 推荐:

浅析基于Spring Security 的身份认证流程

- - 掘金 后端
在企业级项目中,目前较为流行的的认证和授权框架主要是 Spring Security 和 Shiro. Spring Security 相对于 Shiro 具有更加丰富的功能和社区资源,但相对而言 Spring Security 的上手难度也要大于 Shiro. 因此,一般中大型项目会更倾向于使用 Spring Security 框架,而小型项目多采用 Shiro 框架.

cxf + spring 的WS Security示例

- - RSS - IT博客云
WSPasswordCallback的 passwordType属性和 password属性都为null,你只能获得用户名(identifier),一般这里的逻辑是使用这个用户名到数据库中查询其密码,然后再设置到 password属性,WSS4J会自动比较客户端传来的值和你设置的这个值. 你可能会问为什么这里CXF不把客户端提交的密码传入让我们在 ServerPasswordCallbackHandler中比较呢.

Spring Security 实战干货:图解Spring Security中的Servlet过滤器体系

- - SegmentFault 最新的文章
我在 Spring Security 实战干货:内置 Filter 全解析对 Spring Security的内置过滤器进行了罗列,但是 Spring Security真正的过滤器体系才是我们了解它是如何进行"认证"、“授权”、“防止利用漏洞”的关键. Servlet Filter体系. 这里我们以 Servlet Web为讨论目标, Reactive Web暂不讨论.

Spring Boot 使用Spring security 集成CAS - CSDN博客

- -
      创建Maven工程:springboot-security-cas.       创建工程后,打开pom.xml,在pom.xml中加入以下内容:.           .           . 3.创建application.properties.

奔的家园 | CAS 与 Spring Security 3.1整合配置详解

- - Delicious/searchfull
该方式通过获取CAS系统里的角色,来支持CAS与Spring Security的关联,注意该文章authorities部分. 一般来说,Web 应用的安全性包括用户认证(Authentication)和用户授权(Authorization)两个部分. 用户认证指的是验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统.

spring security 3.1.0 控制用户重复登陆

- - CSDN博客架构设计推荐文章
通过配置我们可以实现两个需求 1、限制不允许第二个用户登录,2、第二个登陆用户踢掉前一个登陆用户 . 假设你的spring架构已经可以使用了(其他的主要功能完成),需要增加登录限制功能. 注:这里只写配置不写原理(不懂的就问度娘),其实个人认为先配置好跑起来再研究下原理最好了. 通过sessionRegistry可以获取系统当前在线人数和登录用户信息.

spring security 3中推荐使用BCrypt算法加密密码

- - jackyrong
spring security 3中推荐使用BCrypt算法加密密码了,以前使用的是md5,. Md5PasswordEncoder 和 ShaPasswordEncoder,现在不推荐了,推荐用bcrpt. Bcrpt中的salt可以是随机的,比如:.   其中strenth为长度. 已有 0 人发表留言,猛击->> 这里<<-参与讨论.

Spring security oauth2最简单入门环境搭建--二、干货

- - ITeye博客
关于OAuth2的一些简介,见我的上篇blog: http://wwwcomy.iteye.com/blog/2229889 PS:貌似内容太水直接被鹳狸猿干沉. 友情提示 学习曲线:spring+spring mvc+spring security+Oauth2基本姿势,如果前面都没看过请及时关闭本网页.

Spring Security判断用户是否已经登录 - 简书

- -
方法一、JSP中检查user principal. 需要:.

盘点 Spring Security 框架中的八大经典设计模式

- - SegmentFault 最新的文章
上次有小伙伴建议,源码分析太枯燥了,要是能够结合设计模式一起来,这样更有助于大家理解 Spring Security 源码,同时还能复习一波设计模式. 因此松哥今天就试着整一篇,和大家来聊一聊 Spring Security 中涉及到的设计模式,不过 Spring Security 中涉及到的设计模式还是非常多的,松哥这里讲几个,剩下的欢迎小伙伴们留言补充.