e盾网络验证源码_SpringSecurity源码解析-执行流程

fab15f5e875144c4dc52bc15296e8651.png

SpringSecurity是由一个个过滤器构成,首先在安全框架的最前端的过滤器是SecurityContextPersistenceFilter类,这个类用来存验证信息用。

e05e7a0bf0a18689671444be917079da.png

过滤器会经过UsernamePasswordAuthenticationFilter这个类: 这个类实现的是AbstractAuthenticationProcessingFilter,用doFilter 干了一些事情,主要是逐层调用验证级别我们来看看doFilter干了什么?注意一下,在这个调用链中,一部出错是通过抛出异常来体现验证是否通过的

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
      throws IOException, ServletException {

   HttpServletRequest request = (HttpServletRequest) req;
   HttpServletResponse response = (HttpServletResponse) res;

   if (!requiresAuthentication(request, response)) {
      chain.doFilter(request, response);

      return;
   }

   if (logger.isDebugEnabled()) {
      logger.debug("Request is to process authentication");
   }

   Authentication authResult;

   try {
      authResult = attemptAuthentication(request, response);//注意这句话,很关键,这个是SpringSecurity校验入口,不过在这个抽象类里是没有相应的实现,要到他的子类去找,也就是UsernamePasswordAuthenticationFilter,我们接下来看看
      if (authResult == null) {
         // return immediately as subclass has indicated that it hasn't completed
         // authentication
         return;
      }
      sessionStrategy.onAuthentication(authResult, request, response);
   }
   catch (InternalAuthenticationServiceException failed) {
      logger.error(
            "An internal error occurred while trying to authenticate the user.",
            failed);
      unsuccessfulAuthentication(request, response, failed);

      return;
   }
   catch (AuthenticationException failed) {
      // Authentication failed
      unsuccessfulAuthentication(request, response, failed);

      return;
   }

   // Authentication success
   if (continueChainBeforeSuccessfulAuthentication) {
      chain.doFilter(request, response);
   }

   successfulAuthentication(request, response, chain, authResult);
}

526cf801e2c6b65d86b2137a28f3f87f.png

在这里我们看看在AbstractAuthenticationProcessingFilter里做了什么: 我们在上一部分,知道在他的doFilter里调用了attemptAuthentication,来实现验证

public Authentication attemptAuthentication(HttpServletRequest request,
      HttpServletResponse response) throws AuthenticationException {
   if (postOnly && !request.getMethod().equals("POST")) {
      throw new AuthenticationServiceException(
            "Authentication method not supported: " + request.getMethod());
   }

   String username = obtainUsername(request);//这里是从参数里获取用户名,源码很简单,就是从request里获取参数,request.getParameter(usernameParameter);
   String password = obtainPassword(request);//这里是从参数中获取password

   if (username == null) {
      username = "";
   }

   if (password == null) {
      password = "";
   }

   username = username.trim();

   UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
         username, password);

   // Allow subclasses to set the "details" property
   setDetails(request, authRequest);//将request放入authRequest中,拱使用

   return this.getAuthenticationManager().authenticate(authRequest);//开始验证authRequest,如果不抛出异常,说明验证成功,返回的结果,供doFilter方法中的successfulAuthentication(request, response, chain, authResult)使用
}

9ba2839abc65450f0138e693a04664f9.png

this.getAuthenticationManager().authenticate(authRequest)这句话分析: this.getAuthenticationManager()是用父类AuthenticationManager指向他的子类,他的子类其实是ProviderManager,我们调用ProviderManager中的authenticate来实现验证;源码如下,首先ProviderManager获取所有的AuthenticationProvider,整个AuthenticationProvider实际用来验证的,这个类里有个方法provider.authenticate(authentication);用来做验证。我们来看看AuthenticationProvider他的子类AbstractUserDetailsAuthenticationProvider,DaoAuthenticationProvider干什么事情了。

throws AuthenticationException {
   Class<? extends Authentication> toTest = authentication.getClass();
   AuthenticationException lastException = null;
   Authentication result = null;
   boolean debug = logger.isDebugEnabled();

   for (AuthenticationProvider provider : getProviders()) {
      if (!provider.supports(toTest)) {
         continue;
      }

      if (debug) {
         logger.debug("Authentication attempt using "
               + provider.getClass().getName());
      }

      try {
         result = provider.authenticate(authentication);

         if (result != null) {
            copyDetails(authentication, result);
            break;
         }
      }
      catch (AccountStatusException e) {
         prepareException(e, authentication);
         // SEC-546: Avoid polling additional providers if auth failure is due to
         // invalid account status
         throw e;
      }
      catch (InternalAuthenticationServiceException e) {
         prepareException(e, authentication);
         throw e;
      }
      catch (AuthenticationException e) {
         lastException = e;
      }
   }

   if (result == null && parent != null) {
      // Allow the parent to try.
      try {
         result = parent.authenticate(authentication);
      }
      catch (ProviderNotFoundException e) {
         // ignore as we will throw below if no other exception occurred prior to
         // calling parent and the parent
         // may throw ProviderNotFound even though a provider in the child already
         // handled the request
      }
      catch (AuthenticationException e) {
         lastException = e;
      }
   }

   if (result != null) {
      if (eraseCredentialsAfterAuthentication
            && (result instanceof CredentialsContainer)) {
         // Authentication is complete. Remove credentials and other secret data
         // from authentication
         ((CredentialsContainer) result).eraseCredentials();
      }

      eventPublisher.publishAuthenticationSuccess(result);
      return result;
   }

   // Parent was null, or didn't authenticate (or throw an exception).

   if (lastException == null) {
      lastException = new ProviderNotFoundException(messages.getMessage(
            "ProviderManager.providerNotFound",
            new Object[] { toTest.getName() },
            "No AuthenticationProvider found for {0}"));
   }

   prepareException(lastException, authentication);

   throw lastException;
}

DaoAuthenticationProvider,AbstractUserDetailsAuthenticationProvider,他的验证是写在他的父类抽象类里,他继承过来使用了;retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);DaoAuthenticationProvider写了实现retrieveUser的方法,主要是来校验信息

这个方法写在AbstractUserDetailsAuthenticationProvider

public Authentication authenticate(Authentication authentication)
      throws AuthenticationException {
   Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
         messages.getMessage(
               "AbstractUserDetailsAuthenticationProvider.onlySupports",
               "Only UsernamePasswordAuthenticationToken is supported"));

   // Determine username
   String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED"
         : authentication.getName();

   boolean cacheWasUsed = true;
   UserDetails user = this.userCache.getUserFromCache(username);

   if (user == null) {
      cacheWasUsed = false;

      try {
         user = retrieveUser(username,
               (UsernamePasswordAuthenticationToken) authentication);
      }
      catch (UsernameNotFoundException notFound) {
         logger.debug("User '" + username + "' not found");

         if (hideUserNotFoundExceptions) {
            throw new BadCredentialsException(messages.getMessage(
                  "AbstractUserDetailsAuthenticationProvider.badCredentials",
                  "Bad credentials"));
         }
         else {
            throw notFound;
         }
      }

      Assert.notNull(user,
            "retrieveUser returned null - a violation of the interface contract");
   }

   try {
      preAuthenticationChecks.check(user);//做了是否过期,是否有权限校验
      additionalAuthenticationChecks(user,
            (UsernamePasswordAuthenticationToken) authentication);//验证密码是否过期
   }
   catch (AuthenticationException exception) {
      if (cacheWasUsed) {
         // There was a problem, so try again after checking
         // we're using latest data (i.e. not from the cache)
         cacheWasUsed = false;
         user = retrieveUser(username,
               (UsernamePasswordAuthenticationToken) authentication);
         preAuthenticationChecks.check(user);
         additionalAuthenticationChecks(user,
               (UsernamePasswordAuthenticationToken) authentication);
      }
      else {
         throw exception;
      }
   }

   postAuthenticationChecks.check(user);

   if (!cacheWasUsed) {
      this.userCache.putUserInCache(user);
   }

   Object principalToReturn = user;

   if (forcePrincipalAsString) {
      principalToReturn = user.getUsername();
   }

   return createSuccessAuthentication(principalToReturn, authentication, user);
}

retrieveUser的方法

这个方法写在DaoAuthenticationProvider

protected final UserDetails retrieveUser(String username,
      UsernamePasswordAuthenticationToken authentication)
      throws AuthenticationException {
   UserDetails loadedUser;

   try {
      loadedUser = this.getUserDetailsService().loadUserByUsername(username);//从数据库获取真正的用户名和密码,这个DetailsService是自己重写的方法,如下图
   }
   catch (UsernameNotFoundException notFound) {
      if (authentication.getCredentials() != null) {
         String presentedPassword = authentication.getCredentials().toString();
         passwordEncoder.isPasswordValid(userNotFoundEncodedPassword,
               presentedPassword, null);
      }
      throw notFound;
   }
   catch (Exception repositoryProblem) {
      throw new InternalAuthenticationServiceException(
            repositoryProblem.getMessage(), repositoryProblem);
   }

   if (loadedUser == null) {
      throw new InternalAuthenticationServiceException(
            "UserDetailsService returned null, which is an interface contract violation");
   }
   return loadedUser;
}

a75578ea806cfcfd73830de8fca75e7c.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值