springSecurity认证流程

  • 基于EL-ADMIN项目,剖析验证过程

spring-security的认证机制

  1. 第一步获取登录时候的用户和密码
	UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userInfo.getUsername(), userInfo.getPassword());

这一步如果是自己写的controller就是这么写,其实springSecurity里面是有一个Filter(UsernamePasswordAuthenticationFilter)

	public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        if (this.postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        } else {
            String username = this.obtainUsername(request);
            username = username != null ? username : "";
            username = username.trim();
            String password = this.obtainPassword(request);
            password = password != null ? password : "";
            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
            this.setDetails(request, authRequest);
            return this.getAuthenticationManager().authenticate(authRequest);
        }
    }

UsernamePasswordAuthenticationFilter的这个public Authentication attemptAuthentication()方法通过从表单或者ajax中获取的用户名和密码信息,然后返回一个Authentication对象,用于之后从缓存或者数据库读出来的真实数据进行对比

  1. 进行登录验证
    1. 有一点需要说明的是AuthenticationManagerBuilder的自动注入问题,具体看bloghttps://www.cnblogs.com/felordcn/p/14325124.html 说的是在AuthenticationConfiguration这个配置类中注入的(我没看懂)
    2. authenticationManagerBuilder.getObject()获取的是AuthenticationManager对象
	Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken);
3. authenticationManager.authenticate(authenticationToken)方法的调用
AuthenticationManager是一个接口,调用的是它的实现类ProviderManager中的authenticate()方法
	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();
        Iterator var9 = this.getProviders().iterator();

        while(var9.hasNext()) {
            AuthenticationProvider provider = (AuthenticationProvider)var9.next();
            if (provider.supports(toTest)) {
                if (logger.isTraceEnabled()) {
                    Log var10000 = logger;
                    String var10002 = provider.getClass().getSimpleName();
                    ++currentPosition;
                    var10000.trace(LogMessage.format("Authenticating request with %s (%d/%d)", var10002, currentPosition, size));
                }

                try {
                	// 这里调用AuthenticationProvider provider的authenticate()方法
                	// AuthenticationProvider是一个接口调用的实际上是AbstractUserDetailsAuthenticationProvider 的authenticate()
                	// 详细的代码见下面
                    // 后续就是利用传入的authentication和resulet对象进行比较看是否正确了
                    result = provider.authenticate(authentication);
                    if (result != null) {
                        this.copyDetails(authentication, result);
                        break;
                    }
                } catch (InternalAuthenticationServiceException | AccountStatusException var14) {
                    this.prepareException(var14, authentication);
                    throw var14;
                } catch (AuthenticationException var15) {
                    lastException = var15;
                }
            }
        }

        if (result == null && this.parent != null) {
            try {
                parentResult = this.parent.authenticate(authentication);
                result = parentResult;
            } catch (ProviderNotFoundException var12) {
            } catch (AuthenticationException var13) {
                parentException = var13;
                lastException = var13;
            }
        }

        if (result != null) {
            if (this.eraseCredentialsAfterAuthentication && result instanceof CredentialsContainer) {
                ((CredentialsContainer)result).eraseCredentials();
            }

            if (parentResult == null) {
                this.eventPublisher.publishAuthenticationSuccess(result);
            }

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

            if (parentException == null) {
                this.prepareException((AuthenticationException)lastException, authentication);
            }

            throw lastException;
        }
    }

AbstractUserDetailsAuthenticationProvider 的authenticate()的调用

	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication, () -> {
            return this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports", "Only UsernamePasswordAuthenticationToken is supported");
        });
        // 从传进来的authentication对象中取出username
        String username = this.determineUsername(authentication);
        boolean cacheWasUsed = true;
        // 先从缓存中尝试是否有这个username的用户的存在
        UserDetails user = this.userCache.getUserFromCache(username);
        // 如果没有就会使用retrieveUser()方法,这个方式实质上就是去调用loadUserByUsername()方法
        if (user == null) {
            cacheWasUsed = false;

            try {
            	// 详细见下面的代码
                user = this.retrieveUser(username, (UsernamePasswordAuthenticationToken)authentication);
            } catch (UsernameNotFoundException var6) {
                this.logger.debug("Failed to find user '" + username + "'");
                if (!this.hideUserNotFoundExceptions) {
                    throw var6;
                }

                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);
            this.additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken)authentication);
        } catch (AuthenticationException var7) {
            if (!cacheWasUsed) {
                throw var7;
            }

            cacheWasUsed = false;
            user = this.retrieveUser(username, (UsernamePasswordAuthenticationToken)authentication);
            this.preAuthenticationChecks.check(user);
            this.additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken)authentication);
        }

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

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

        return this.createSuccessAuthentication(principalToReturn, authentication, user);
    }

retrieveUser()方法的代码

	// 这个是AbstractUserDetailsAuthenticationProvider这个抽象类的抽象方法,实际上是调用的其子类DaoAuthenticationProvider的方法重写
	// protected abstract UserDetails retrieveUser(String var1, UsernamePasswordAuthenticationToken var2) throws AuthenticationException;

	protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        this.prepareTimingAttackProtection();

        try {
        	// 这里就是调用的UserDetailService这个接口里面的loadUserByUsername(String s)方法.
        	// loadUserByUsername(String s)是我们自己写的一个实现类。逻辑大概就是从缓存或者是从数据库查找,返回一个UserDetail的对象
        	// UserDetail是一个接口,其中spring-security自己里面实现的一个类是User,也可以自定义
            UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
            if (loadedUser == null) {
                throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");
            } else {
                return loadedUser;
            }
        } catch (UsernameNotFoundException var4) {
            this.mitigateAgainstTimingAttack(authentication);
            throw var4;
        } catch (InternalAuthenticationServiceException var5) {
            throw var5;
        } catch (Exception var6) {
            throw new InternalAuthenticationServiceException(var6.getMessage(), var6);
        }
    }
	@Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        UserInfo userInfo = userInfodao.findByUsername(s);
        List<GrantedAuthority> list = new ArrayList<GrantedAuthority>();
        if(userInfo.getRole() != null){
            list.add(new SimpleGrantedAuthority(userInfo.getRole()));
        }
        return new User(userInfo.getUsername(),  new BCryptPasswordEncoder().encode(userInfo.getPassword()),list);
    }
  1. 验证成功之后将Authentication对象存到SecurityContext中
	SecurityContextHolder.getContext().setAuthentication(authentication);

该blog参考了https://www.cnblogs.com/ymstars/p/10626786.html

posted @ 2021-04-19 22:14  codeDJH  阅读(589)  评论(0编辑  收藏  举报