Spring Security 源码解析

传统情况下,在过滤器中做权限验证,Spring Secuirty也是在Filter中进行权限验证。

创建并注册过滤器

package com.awizdata.edubank.config;

import com.awizdata.edubank.security.JwtAuthenticationTokenFilter;
import com.awizdata.edubank.security.JwtLoginFilter;
import com.awizdata.edubank.security.JwtUserDetailsServiceImpl;
import com.awizdata.edubank.security.matches.SkipPathRequestMatcher;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

import java.util.Arrays;
import java.util.List;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    public static final String TOKEN_BASED_ENTRY_POINT = "/limit/**";
    public static final String TOKEN_AUTH_ENTRY_POINT = "/auth/**";
    public static final String TOKEN_OPEN_ENTRY_POINT = "/open/**";

    @Bean
    public SkipPathRequestMatcher skipPathRequestMatcher() {
        List<String> pathsToSkip = Arrays.asList(TOKEN_AUTH_ENTRY_POINT);
        return new SkipPathRequestMatcher(pathsToSkip, TOKEN_BASED_ENTRY_POINT);
    }

    @Bean
    public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter() {
        return new JwtAuthenticationTokenFilter(skipPathRequestMatcher());
    }

    //创建登录过滤器
    @Bean
    public JwtLoginFilter jwtLoginFilter() {
        return new JwtLoginFilter(authenticationManager());
    }

    //重写userDetailsService,查询数据库进行用户名密码验证
    @Override
    @Bean
    public UserDetailsService userDetailsService() {
        return new JwtUserDetailsServiceImpl();
    }

    @Bean
    public DaoAuthenticationProvider daoAuthenticationProvider() {
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        daoAuthenticationProvider.setPasswordEncoder(this.passwordEncoder());
        daoAuthenticationProvider.setUserDetailsService(this.userDetailsService());
        return daoAuthenticationProvider;
    }

    @Override
    @Bean
    public AuthenticationManager authenticationManager() {
        return new ProviderManager(Arrays.asList(daoAuthenticationProvider()));
    }

    // 装载BCrypt密码编码器
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
                .cors().and()
                // 由于使用的是JWT,我们这里不需要csrf
                .csrf().disable()
                // 基于token,所以不需要session, Spring Security永远不会创建HttpSession,它不会使用HttpSession来获取SecurityContext
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                // 允许对于网站静态资源的无授权访问
                .antMatchers(HttpMethod.GET,
                        "/",
                        "/error",
                        "/*.html",
                        "/favicon.ico",
                        "/**/*.html",
                        "/**/*.css",
                        "/**/*.js"
                        ).permitAll()
                // 对于获取token的rest api要允许匿名访问
                .antMatchers(TOKEN_AUTH_ENTRY_POINT, TOKEN_OPEN_ENTRY_POINT).permitAll()
                // 除上面外的所有请求全部需要鉴权认证
                .anyRequest().authenticated()
                .and()
                 //使用自定义过滤器
                .addFilter(this.jwtLoginFilter())
                .addFilterAfter(jwtAuthenticationTokenFilter(), JwtLoginFilter.class);
        // 禁用缓存
        httpSecurity.headers().cacheControl();
    }
}

 2 Spring Security校验流程图

 Spring Security验证的本质:用户登录后获得用户名和密码,根据用户名获取用户信息,并比对密码判断用户能否验证通过,并在此过程中对帐号是否已被锁定、账号是否可用、帐号是否已经过期、用户凭证是否已经过期。下面根据代码跟踪进一步分析验证过程

 3源码分析

1 AbstractAuthenticationProcessingFilter.class

用户登录访问首先访问此过滤器,并调用doFilter方法

 1 public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
 2         HttpServletRequest request = (HttpServletRequest)req;
 3         HttpServletResponse response = (HttpServletResponse)res;
 4         //判断访问是否需要过滤
 5         if(!this.requiresAuthentication(request, response)) {
 6             chain.doFilter(request, response);
 7         } else {
 8             if(this.logger.isDebugEnabled()) {
 9                 this.logger.debug("Request is to process authentication");
10             }
11 
12             Authentication authResult;
13             //调用子类UsernamePasswordAuthenticationFilter实现
14             try {
15                 authResult = this.attemptAuthentication(request, response);
16                 if(authResult == null) {
17                     return;
18                 }
19                 //session控制策略,因为用jwt,故不进行深入分析
20                 this.sessionStrategy.onAuthentication(authResult, request, response);
21             } catch (InternalAuthenticationServiceException var8) {
22                 this.logger.error("An internal error occurred while trying to authenticate the user.", var8);
23                 //验证失败,调用子类unsuccessfulAuthentication方法
24                 this.unsuccessfulAuthentication(request, response, var8);
25                 return;
26             } catch (AuthenticationException var9) {
27                 //验证失败,调用子类unsuccessfulAuthentication方法
28                 this.unsuccessfulAuthentication(request, response, var9);
29                 return;
30             }
31 
32             if(this.continueChainBeforeSuccessfulAuthentication) {
33                 chain.doFilter(request, response);
34             }
35             //验证成功,调用子类successfulAuthentication方法
36             this.successfulAuthentication(request, response, chain, authResult);
37         }
38     }

2 UsernamePasswordAuthenticationFilter

AbstractAuthenticationProcessingFilter 的 doFilter 方法中调用了 UsernamePasswordAuthenticationFilter 的 attemptAuthentication方法

 1 public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
 2     public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
 3     public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
 4     private String usernameParameter = "username";
 5     private String passwordParameter = "password";
 6     private boolean postOnly = true;
 7 
 8     //拦截POST形式的/login请求
 9     public UsernamePasswordAuthenticationFilter() {
10         super(new AntPathRequestMatcher("/login", "POST"));
11     }
12 
13     public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
14         if(this.postOnly && !request.getMethod().equals("POST")) {
15             throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
16         } else {
17             //获取Parameter中的用户名和密码
18             String username = this.obtainUsername(request);
19             String password = this.obtainPassword(request);
20             if(username == null) {
21                 username = "";
22             }
23 
24             if(password == null) {
25                 password = "";
26             }
27 
28             username = username.trim();
29             //封装用户名和密码
30             UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
31             this.setDetails(request, authRequest);
32             //调用子类getAuthenticationManager()方法获取AuthenticationManager并调用起authenticate方法进行验证
33             return this.getAuthenticationManager().authenticate(authRequest);
34         }
35     }
36 
37     protected String obtainPassword(HttpServletRequest request) {
38         return request.getParameter(this.passwordParameter);
39     }
40 
41     protected String obtainUsername(HttpServletRequest request) {
42         return request.getParameter(this.usernameParameter);
43     }
44 
45     //记录remoteAddress、sessionId
46     protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {
47         authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
48     }
49 
50     public void setUsernameParameter(String usernameParameter) {
51         Assert.hasText(usernameParameter, "Username parameter must not be empty or null");
52         this.usernameParameter = usernameParameter;
53     }
54 
55     public void setPasswordParameter(String passwordParameter) {
56         Assert.hasText(passwordParameter, "Password parameter must not be empty or null");
57         this.passwordParameter = passwordParameter;
58     }
59 
60     public void setPostOnly(boolean postOnly) {
61         this.postOnly = postOnly;
62     }
63 
64     public final String getUsernameParameter() {
65         return this.usernameParameter;
66     }
67 
68     public final String getPasswordParameter() {
69         return this.passwordParameter;
70     }
71 }

 3 ProviderManager

UsernamePasswordAuthenticationFilter 中调用this.getAuthenticationManager().authenticate(authRequest);从WebSecurityConfig(见下图) 可以发现this.getAuthenticationManager().authenticate(authRequest)调用ProviderManager的authenticate方法,
并在authenticate方法中调用了daoAuthenticationProvider的authenticate方法

  1 //
  2 // Source code recreated from a .class file by IntelliJ IDEA
  3 // (powered by Fernflower decompiler)
  4 //
  5 
  6 package org.springframework.security.authentication;
  7 
  8 import java.util.Collections;
  9 import java.util.Iterator;
 10 import java.util.List;
 11 import org.apache.commons.logging.Log;
 12 import org.apache.commons.logging.LogFactory;
 13 import org.springframework.beans.factory.InitializingBean;
 14 import org.springframework.context.MessageSource;
 15 import org.springframework.context.MessageSourceAware;
 16 import org.springframework.context.support.MessageSourceAccessor;
 17 import org.springframework.security.authentication.AbstractAuthenticationToken;
 18 import org.springframework.security.authentication.AccountStatusException;
 19 import org.springframework.security.authentication.AuthenticationEventPublisher;
 20 import org.springframework.security.authentication.AuthenticationManager;
 21 import org.springframework.security.authentication.AuthenticationProvider;
 22 import org.springframework.security.authentication.InternalAuthenticationServiceException;
 23 import org.springframework.security.authentication.ProviderNotFoundException;
 24 import org.springframework.security.core.Authentication;
 25 import org.springframework.security.core.AuthenticationException;
 26 import org.springframework.security.core.CredentialsContainer;
 27 import org.springframework.security.core.SpringSecurityMessageSource;
 28 import org.springframework.util.Assert;
 29 
 30 public class ProviderManager implements AuthenticationManager, MessageSourceAware, InitializingBean {
 31     private static final Log logger = LogFactory.getLog(ProviderManager.class);
 32     private AuthenticationEventPublisher eventPublisher;
 33     private List<AuthenticationProvider> providers;
 34     protected MessageSourceAccessor messages;
 35     private AuthenticationManager parent;
 36     private boolean eraseCredentialsAfterAuthentication;
 37 
 38     public ProviderManager(List<AuthenticationProvider> providers) {
 39         this(providers, (AuthenticationManager)null);
 40     }
 41 
 42     public ProviderManager(List<AuthenticationProvider> providers, AuthenticationManager parent) {
 43         this.eventPublisher = new ProviderManager.NullEventPublisher();
 44         this.providers = Collections.emptyList();
 45         this.messages = SpringSecurityMessageSource.getAccessor();
 46         this.eraseCredentialsAfterAuthentication = true;
 47         Assert.notNull(providers, "providers list cannot be null");
 48         this.providers = providers;
 49         this.parent = parent;
 50         this.checkState();
 51     }
 52 
 53     public void afterPropertiesSet() throws Exception {
 54         this.checkState();
 55     }
 56 
 57     private void checkState() {
 58         if(this.parent == null && this.providers.isEmpty()) {
 59             throw new IllegalArgumentException("A parent AuthenticationManager or a list of AuthenticationProviders is required");
 60         }
 61     }
 62 
 63     public Authentication authenticate(Authentication authentication) throws AuthenticationException {
 64         
 65         Class toTest = authentication.getClass();//org.springframework.security.authentication.UsernamePasswordAuthenticationToken
 66         Object lastException = null;
 67         Authentication result = null;
 68         boolean debug = logger.isDebugEnabled();
 69         //获得authenticationManager列表这里只有daoAuthenticationProvider
 70         Iterator e = this.getProviders().iterator();
 71 
 72         while(e.hasNext()) {
 73             AuthenticationProvider provider = (AuthenticationProvider)e.next();
 74             //authenticationProvider是否为UsernamePasswordAuthenticationToken或其子类
 75             if(provider.supports(toTest)) {
 76                 if(debug) {
 77                     logger.debug("Authentication attempt using " + provider.getClass().getName());
 78                 }
 79 
 80                 try {
 81                     result = provider.authenticate(authentication);
 82                     //copy Details到authentication
 83                     if(result != null) {
 84                         this.copyDetails(authentication, result);
 85                         break;
 86                     }
 87                 } catch (AccountStatusException var11) {
 88                     this.prepareException(var11, authentication);
 89                     throw var11;
 90                 } catch (InternalAuthenticationServiceException var12) {
 91                     this.prepareException(var12, authentication);
 92                     throw var12;
 93                 } catch (AuthenticationException var13) {
 94                     lastException = var13;
 95                 }
 96             }
 97         }
 98 
 99         //未设置AuthenticationProvider的情况下,调用parent进行校验,这里parent未指定
100         if(result == null && this.parent != null) {
101             try {
102                 result = this.parent.authenticate(authentication);
103             } catch (ProviderNotFoundException var9) {
104                 ;
105             } catch (AuthenticationException var10) {
106                 lastException = var10;
107             }
108         }
109 
110         if(result != null) {
111             if(this.eraseCredentialsAfterAuthentication && result instanceof CredentialsContainer) {
112                 ((CredentialsContainer)result).eraseCredentials();
113             }
114 
115             //NullEventPublisher实现此方法,但未做任何处理
116             this.eventPublisher.publishAuthenticationSuccess(result);
117             return result;
118         } else {
119             if(lastException == null) {
120                 lastException = new ProviderNotFoundException(this.messages.getMessage("ProviderManager.providerNotFound", new Object[]{toTest.getName()}, "No AuthenticationProvider found for {0}"));
121             }
122 
123             this.prepareException((AuthenticationException)lastException, authentication);
124             throw lastException;
125         }
126     }
127 
128     private void prepareException(AuthenticationException ex, Authentication auth) {
129         this.eventPublisher.publishAuthenticationFailure(ex, auth);
130     }
131 
132     private void copyDetails(Authentication source, Authentication dest) {
133         if(dest instanceof AbstractAuthenticationToken && dest.getDetails() == null) {
134             AbstractAuthenticationToken token = (AbstractAuthenticationToken)dest;
135             token.setDetails(source.getDetails());
136         }
137 
138     }
139 
140     public List<AuthenticationProvider> getProviders() {
141         return this.providers;
142     }
143 
144     public void setMessageSource(MessageSource messageSource) {
145         this.messages = new MessageSourceAccessor(messageSource);
146     }
147 
148     public void setAuthenticationEventPublisher(AuthenticationEventPublisher eventPublisher) {
149         Assert.notNull(eventPublisher, "AuthenticationEventPublisher cannot be null");
150         this.eventPublisher = eventPublisher;
151     }
152 
153     public void setEraseCredentialsAfterAuthentication(boolean eraseSecretData) {
154         this.eraseCredentialsAfterAuthentication = eraseSecretData;
155     }
156 
157     public boolean isEraseCredentialsAfterAuthentication() {
158         return this.eraseCredentialsAfterAuthentication;
159     }
160 
161     private static final class NullEventPublisher implements AuthenticationEventPublisher {
162         private NullEventPublisher() {
163         }
164 
165         public void publishAuthenticationFailure(AuthenticationException exception, Authentication authentication) {
166         }
167 
168         public void publishAuthenticationSuccess(Authentication authentication) {
169         }
170     }
171 }

 4 AbstractUserDetailsAuthenticationProvider

daoAuthenticationProvider类并未实现authenticate方法,故这里调用了其父类(抽象类)AbstractUserDetailsAuthenticationProvider的authenticate方法
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.security.authentication.dao;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper;
import org.springframework.security.core.userdetails.UserCache;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsChecker;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.core.userdetails.cache.NullUserCache;
import org.springframework.util.Assert;

public abstract class AbstractUserDetailsAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware {
    protected final Log logger = LogFactory.getLog(this.getClass());
    protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
    private UserCache userCache = new NullUserCache();
    private boolean forcePrincipalAsString = false;
    protected boolean hideUserNotFoundExceptions = true;
    private UserDetailsChecker preAuthenticationChecks = new AbstractUserDetailsAuthenticationProvider.DefaultPreAuthenticationChecks();
    private UserDetailsChecker postAuthenticationChecks = new AbstractUserDetailsAuthenticationProvider.DefaultPostAuthenticationChecks();
    private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();

    public AbstractUserDetailsAuthenticationProvider() {
    }

    protected abstract void additionalAuthenticationChecks(UserDetails var1, UsernamePasswordAuthenticationToken var2) throws AuthenticationException;

    public final void afterPropertiesSet() throws Exception {
        Assert.notNull(this.userCache, "A user cache must be set");
        Assert.notNull(this.messages, "A message source must be set");
        this.doAfterPropertiesSet();
    }

    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        //类型判断
        Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication, this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports", "Only UsernamePasswordAuthenticationToken is supported"));
        //提取username
        String username = authentication.getPrincipal() == null?"NONE_PROVIDED":authentication.getName();
        //使用缓存
        boolean cacheWasUsed = true;
        //根据username从缓存中获取User信息
        UserDetails user = this.userCache.getUserFromCache(username);
        //缓存中不存在对应User信息,则重新从数据库加载
        if(user == null) {
            cacheWasUsed = false;

            try {
                //从数据库加载User信息
                user = this.retrieveUser(username, (UsernamePasswordAuthenticationToken)authentication);
            } catch (UsernameNotFoundException var6) {
                this.logger.debug("User \'" + username + "\' not found");
                if(this.hideUserNotFoundExceptions) {
                    throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
                }

                throw var6;
            }

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

        try {
            //校验账号是否被锁定,是否可用,是否过期
            this.preAuthenticationChecks.check(user);
            //判断密码是否匹配(DaoAuthenticationProvider中实现)
            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();
        }

        //生成UsernamePasswordAuthenticationToken返回
        return this.createSuccessAuthentication(principalToReturn, authentication, user);
    }

    protected Authentication createSuccessAuthentication(Object principal, Authentication authentication, UserDetails user) {
        UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(principal, authentication.getCredentials(), this.authoritiesMapper.mapAuthorities(user.getAuthorities()));
        result.setDetails(authentication.getDetails());
        return result;
    }

    protected void doAfterPropertiesSet() throws Exception {
    }

    public UserCache getUserCache() {
        return this.userCache;
    }

    public boolean isForcePrincipalAsString() {
        return this.forcePrincipalAsString;
    }

    public boolean isHideUserNotFoundExceptions() {
        return this.hideUserNotFoundExceptions;
    }

    protected abstract UserDetails retrieveUser(String var1, UsernamePasswordAuthenticationToken var2) throws AuthenticationException;

    public void setForcePrincipalAsString(boolean forcePrincipalAsString) {
        this.forcePrincipalAsString = forcePrincipalAsString;
    }

    public void setHideUserNotFoundExceptions(boolean hideUserNotFoundExceptions) {
        this.hideUserNotFoundExceptions = hideUserNotFoundExceptions;
    }

    public void setMessageSource(MessageSource messageSource) {
        this.messages = new MessageSourceAccessor(messageSource);
    }

    public void setUserCache(UserCache userCache) {
        this.userCache = userCache;
    }

    public boolean supports(Class<?> authentication) {
        return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
    }

    protected UserDetailsChecker getPreAuthenticationChecks() {
        return this.preAuthenticationChecks;
    }

    public void setPreAuthenticationChecks(UserDetailsChecker preAuthenticationChecks) {
        this.preAuthenticationChecks = preAuthenticationChecks;
    }

    protected UserDetailsChecker getPostAuthenticationChecks() {
        return this.postAuthenticationChecks;
    }

    public void setPostAuthenticationChecks(UserDetailsChecker postAuthenticationChecks) {
        this.postAuthenticationChecks = postAuthenticationChecks;
    }

    public void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) {
        this.authoritiesMapper = authoritiesMapper;
    }

    private class DefaultPostAuthenticationChecks implements UserDetailsChecker {
        private DefaultPostAuthenticationChecks() {
        }

        public void check(UserDetails user) {
            if(!user.isCredentialsNonExpired()) {
                AbstractUserDetailsAuthenticationProvider.this.logger.debug("User account credentials have expired");
                throw new CredentialsExpiredException(AbstractUserDetailsAuthenticationProvider.this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.credentialsExpired", "User credentials have expired"));
            }
        }
    }

    private class DefaultPreAuthenticationChecks implements UserDetailsChecker {
        private DefaultPreAuthenticationChecks() {
        }

        public void check(UserDetails user) {
            if(!user.isAccountNonLocked()) {
                AbstractUserDetailsAuthenticationProvider.this.logger.debug("User account is locked");
                throw new LockedException(AbstractUserDetailsAuthenticationProvider.this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.locked", "User account is locked"));
            } else if(!user.isEnabled()) {
                AbstractUserDetailsAuthenticationProvider.this.logger.debug("User account is disabled");
                throw new DisabledException(AbstractUserDetailsAuthenticationProvider.this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.disabled", "User is disabled"));
            } else if(!user.isAccountNonExpired()) {
                AbstractUserDetailsAuthenticationProvider.this.logger.debug("User account is expired");
                throw new AccountExpiredException(AbstractUserDetailsAuthenticationProvider.this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.expired", "User account has expired"));
            }
        }
    }
}

 5 DaoAuthenticationProvider 

抽象类AbstractUserDetailsAuthenticationProvider中 核心方法additionalAuthenticationChecks  retrieveUser 均在DaoAuthenticationProvider 中实现

 1 public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
 2     private static final String USER_NOT_FOUND_PASSWORD = "userNotFoundPassword";
 3     private PasswordEncoder passwordEncoder;
 4     private volatile String userNotFoundEncodedPassword;
 5     private UserDetailsService userDetailsService;
 6 
 7     public DaoAuthenticationProvider() {
 8         this.setPasswordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder());
 9     }
10 
11     //验证密码是否正确
12     protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
13         if(authentication.getCredentials() == null) {
14             this.logger.debug("Authentication failed: no credentials provided");
15             throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
16         } else {
17             String presentedPassword = authentication.getCredentials().toString();
18             if(!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
19                 this.logger.debug("Authentication failed: password does not match stored value");
20                 throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
21             }
22         }
23     }
24 
25     protected void doAfterPropertiesSet() throws Exception {
26         Assert.notNull(this.userDetailsService, "A UserDetailsService must be set");
27     }
28 
29     //根据username 重新请求User信息
30     protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
31         this.prepareTimingAttackProtection();
32 
33         try {
34             //这里自己做实现
35             UserDetails ex = this.getUserDetailsService().loadUserByUsername(username);
36             if(ex == null) {
37                 throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");
38             } else {
39                 return ex;
40             }
41         } catch (UsernameNotFoundException var4) {
42             this.mitigateAgainstTimingAttack(authentication);
43             throw var4;
44         } catch (InternalAuthenticationServiceException var5) {
45             throw var5;
46         } catch (Exception var6) {
47             throw new InternalAuthenticationServiceException(var6.getMessage(), var6);
48         }
49     }

6 UserDetailsService 实现

 1 @Service
 2 public class JwtUserDetailsServiceImpl implements UserDetailsService {
 3 
 4     @Autowired
 5     private RcyUserDAO rcyUserDAO;
 6 
 7     @Override
 8     public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
 9         RcyUserPO user = rcyUserDAO.findByUsername(username);
10         if (user == null) {
11             throw new UsernameNotFoundException(String.format("No user found with username '%s'.", username));
12         } else {
13             return JwtUserFactory.create(user);
14         }
15     }
16 }

 至此,Spring Security涉及源码分析完毕

我们总结一下,使用Spring Security需要做的工作

1 创建过滤器JwtLoginFilter 该类继承UsernamePasswordAuthenticationFilter 并重写successfulAuthentication unsuccessfulAuthentication方法

2 配置WebSecurityConfig 该类继承WebSecurityConfigurerAdapter,并在configure方法中注册过滤器JwtLoginFilter

3 注册Bean JwtLoginFilter、DaoAuthenticationProvider、AuthenticationManager 、UserDetailsService并对UserDetailsService做具体实现

猜你喜欢

转载自www.cnblogs.com/allenli263/p/9817011.html