zoukankan      html  css  js  c++  java
  • SpringSecurity入门案例

    1 入门案例搭建

    1.1 导入相应jar包Maven坐标

    • 修改部分:
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    
    • 完整部分:
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.2.1.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.sunxiaping</groupId>
        <artifactId>spring-security</artifactId>
        <version>1.0</version>
    
        <properties>
            <java.version>1.8</java.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-security</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <scope>runtime</scope>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
                <exclusions>
                    <exclusion>
                        <groupId>org.junit.vintage</groupId>
                        <artifactId>junit-vintage-engine</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>
    

    1.2 启动类

    package com.sunxiaping.springsecurity;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class SpringSecurityApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SpringSecurityApplication.class, args);
        }
    }
    

    1.3 修改配置文件

    • application.properties
    server.port=8181
    

    1.4 运行项目

    • 访问http://localhost:8181,会出现如下的界面:

    SpringSecurity启动出现的界面

    • 默认的用户名是:user。
    • 密码在项目启动的时候会在控制台打印,需要注意的是,每次启动密码都会发生变化

    密码在项目启动的时候会在控制台打印,

    • 输入用户名和密码,就可以访问了,404表示我们没有这个控制器,但是我们可以访问了。

    输入用户名和密码就可以访问了

    2 权限管理中的相关概念

    2.1 主体

    • 英文单词:principal。
    • 使用系统的用户、设备或从其他系统远程登录的用户等等。简单的说,谁使用系统谁就是主体。

    2.2 认证

    • 英文单词:authentication。
    • 权限管理系统确认一个主体的身份,允许主体进入系统。简单的说,主体证明自己是谁。
    • 笼统的认为就是以前所做的登录操作。

    2.3 授权

    • 英文单词:authorization。
    • 将操作系统的权力授予主体,这样主体就具备了操作系统中特定功能的能力。简单的说,授权就是给用户分配权限。

    3 基本原理

    3.1 Spring Security过滤器链介绍

    • Spring Boot启动的时候会加载所有jar包下的META-INF/spring.factories文件,会扫描org.springframework.boot.autoconfigure.EnableAutoConfiguration所对应的值,将其加载到Spring容器中。
    # Auto Configure
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=
    # 其他略
    org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,
    org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,
    org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,
    # 其他略
    
    • SecurityFilterAutoConfiguration的源码如下:
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnWebApplication(type = Type.SERVLET)
    @EnableConfigurationProperties(SecurityProperties.class)
    @ConditionalOnClass({ AbstractSecurityWebApplicationInitializer.class, SessionCreationPolicy.class })
    @AutoConfigureAfter(SecurityAutoConfiguration.class)
    public class SecurityFilterAutoConfiguration {
        //springSecurityFilterChain    
        private static final String DEFAULT_FILTER_NAME = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME;
    
        //向容器中加载DelegatingFilterProxyRegistrationBean的Bean组件,Bean的名字为springSecurityFilterChain
        @Bean
        @ConditionalOnBean(name = DEFAULT_FILTER_NAME)
        public DelegatingFilterProxyRegistrationBean securityFilterChainRegistration(
                SecurityProperties securityProperties) {
            DelegatingFilterProxyRegistrationBean registration = new DelegatingFilterProxyRegistrationBean(
                    DEFAULT_FILTER_NAME);
            registration.setOrder(securityProperties.getFilter().getOrder());
            registration.setDispatcherTypes(getDispatcherTypes(securityProperties));
            return registration;
        }
    
        private EnumSet<DispatcherType> getDispatcherTypes(SecurityProperties securityProperties) {
            if (securityProperties.getFilter().getDispatcherTypes() == null) {
                return null;
            }
            return securityProperties.getFilter().getDispatcherTypes().stream()
                    .map((type) -> DispatcherType.valueOf(type.name()))
                    .collect(Collectors.collectingAndThen(Collectors.toSet(), EnumSet::copyOf));
        }
    
    }
    
    • DelegatingFilterProxyRegistrationBean的源码如下:
    //由此我们可以知道,向容器加载了DelegatingFilterProxy对象
    public class DelegatingFilterProxyRegistrationBean extends AbstractFilterRegistrationBean<DelegatingFilterProxy>
            implements ApplicationContextAware {
        //其他略
    }
    
    • DelegatingFilterProxy的源码如下:
    //public abstract class GenericFilterBean implements Filter
    //我们可以知道DelegatingFilterProxy也是一个过滤器,那么一定有doFilter方法
    public class DelegatingFilterProxy extends GenericFilterBean {
        @Nullable
        private String contextAttribute;
        @Nullable
        private WebApplicationContext webApplicationContext;
        @Nullable
        private String targetBeanName;
        private boolean targetFilterLifecycle;
        @Nullable
        private volatile Filter delegate; //这个才是真正加载的过滤器
        private final Object delegateMonitor;
    
        //过滤器的入口
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException {
            Filter delegateToUse = this.delegate;
            if (delegateToUse == null) {
                synchronized(this.delegateMonitor) {
                    delegateToUse = this.delegate;
                    if (delegateToUse == null) {
                        WebApplicationContext wac = this.findWebApplicationContext();
                        if (wac == null) {
                            throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener or DispatcherServlet registered?");
                        }
                      //调用initDelegate方法,给上面的私有属性delegate初始化    
                        delegateToUse = this.initDelegate(wac);
                    }
    
                    this.delegate = delegateToUse;
                }
            }
    
            this.invokeDelegate(delegateToUse, request, response, filterChain);
        }
    
        //直接看最终加载的过滤器到底是谁
        protected Filter initDelegate(WebApplicationContext wac) throws ServletException {
            //通过debug可以知道,targetBeanName是springSecurityFilterChain
            String targetBeanName = this.getTargetBeanName();
            Assert.state(targetBeanName != null, "No target bean name set");
            //通过debug可以知道delegate是FilterChainProxy
            Filter delegate = (Filter)wac.getBean(targetBeanName, Filter.class);
            if (this.isTargetFilterLifecycle()) {
                delegate.init(this.getFilterConfig());
            }
    
            return delegate;
        }    
    
        //其他略
    
    }    
    

    Debug获取默认的过滤器链

    • FilterChainProxy的源码如下:
    public class FilterChainProxy extends GenericFilterBean {
        private static final Log logger = LogFactory.getLog(FilterChainProxy.class);
        private static final String FILTER_APPLIED = FilterChainProxy.class.getName().concat(".APPLIED");
        private List<SecurityFilterChain> filterChains;
        private FilterChainProxy.FilterChainValidator filterChainValidator;
        private HttpFirewall firewall;
    
        public FilterChainProxy() {
            this.filterChainValidator = new FilterChainProxy.NullFilterChainValidator();
            this.firewall = new StrictHttpFirewall();
        }
    
        //通过SecurityFilterChain对象,初始化List<SecurityFilterChain>
        public FilterChainProxy(SecurityFilterChain chain) {
            this(Arrays.asList(chain));
        }
    
        public FilterChainProxy(List<SecurityFilterChain> filterChains) {
            this.filterChainValidator = new FilterChainProxy.NullFilterChainValidator();
            this.firewall = new StrictHttpFirewall();
            this.filterChains = filterChains;
        }
        //过滤器的入口
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            boolean clearContext = request.getAttribute(FILTER_APPLIED) == null;
            if (clearContext) {
                try {
                    request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
                    this.doFilterInternal(request, response, chain);
                } finally {
                    SecurityContextHolder.clearContext();
                    request.removeAttribute(FILTER_APPLIED);
                }
            } else {
                //调用了doFilterInternal方法
                this.doFilterInternal(request, response, chain);
            }
    
        }
    
        private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            FirewalledRequest fwRequest = this.firewall.getFirewalledRequest((HttpServletRequest)request);
            HttpServletResponse fwResponse = this.firewall.getFirewalledResponse((HttpServletResponse)response);
            //封装要执行的过滤器链,很多过滤器就在这里被封装进去了
            List<Filter> filters = this.getFilters((HttpServletRequest)fwRequest);
            if (filters != null && filters.size() != 0) {
                FilterChainProxy.VirtualFilterChain vfc = new FilterChainProxy.VirtualFilterChain(fwRequest, chain, filters);   
                //加载过滤器链
                vfc.doFilter(fwRequest, fwResponse);
            } else {
                if (logger.isDebugEnabled()) {
                    logger.debug(UrlUtils.buildRequestUrl(fwRequest) + (filters == null ? " has no matching filters" : " has an empty filter list"));
                }
    
                fwRequest.reset();
                chain.doFilter(fwRequest, fwResponse);
            }
        }
        private List<Filter> getFilters(HttpServletRequest request) {
            Iterator var2 = this.filterChains.iterator();
            //封装过滤器链到SecurityFilterChain中
            SecurityFilterChain chain;
            do {
                if (!var2.hasNext()) {
                    return null;
                }
    
                chain = (SecurityFilterChain)var2.next();
            } while(!chain.matches(request));
    
            return chain.getFilters();
        }    
    
    }    
    
    • SecurityFilterChain的源码如下:
    //是接口,唯一的子类是DefaultSecurityFilterChain
    public interface SecurityFilterChain {
        boolean matches(HttpServletRequest var1);
    
        List<Filter> getFilters();
    }
    
    • DefaultSecurityFilterChain的源码如下:
    public final class DefaultSecurityFilterChain implements SecurityFilterChain {
        private static final Log logger = LogFactory.getLog(DefaultSecurityFilterChain.class);
        private final RequestMatcher requestMatcher;
        private final List<Filter> filters;
    
        public DefaultSecurityFilterChain(RequestMatcher requestMatcher, Filter... filters) {
            this(requestMatcher, Arrays.asList(filters));
        }
    
        public DefaultSecurityFilterChain(RequestMatcher requestMatcher, List<Filter> filters) {
            logger.info("Creating filter chain: " + requestMatcher + ", " + filters);
            this.requestMatcher = requestMatcher;
            this.filters = new ArrayList(filters);
        }
    
        public RequestMatcher getRequestMatcher() {
            return this.requestMatcher;
        }
    
        public List<Filter> getFilters() {
            return this.filters;
        }
    
        public boolean matches(HttpServletRequest request) {
            return this.requestMatcher.matches(request);
        }
    
        public String toString() {
            return "[ " + this.requestMatcher + ", " + this.filters + "]";
        }
    }
    

    3.2 Spring Security自定义进行认证原理

    • 默认情况下,账号和密码是由Spring Security自动生成的。在实际项目中的账号和密码需要从数据库中查询出来的。所以我们要通过自定义逻辑控制认证的逻辑。

    • 我们知道,默认的过滤器链中有一个过滤器是UsernamePasswordAuthenticationFilter,其源码如下:

    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() {
            super(new AntPathRequestMatcher("/login", "POST"));
        }
    
        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);
                String password = this.obtainPassword(request);
                if (username == null) {
                    username = "";
                }
    
                if (password == null) {
                    password = "";
                }
    
                username = username.trim();
                //将用户填写的username和password封装到了UsernamePasswordAuthenticationToken中
                UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
                this.setDetails(request, authRequest);
                //调用authenticationManager进行认证
                return this.getAuthenticationManager().authenticate(authRequest);
            }
        }
        protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {
            authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
        }
    
        //其他略
    }
    
    • AuthenticationManager的源码如下:
    //万万没想到,AuthenticationManager是接口,其实现子类有ProviderManager
    public interface AuthenticationManager {
        Authentication authenticate(Authentication var1) throws AuthenticationException;
    }
    
    • ProviderManager的源码如下:
    public class ProviderManager implements AuthenticationManager, MessageSourceAware, InitializingBean {
        private static final Log logger = LogFactory.getLog(ProviderManager.class);
        private AuthenticationEventPublisher eventPublisher;
        private List<AuthenticationProvider> providers;
        protected MessageSourceAccessor messages;
        private AuthenticationManager parent;
        private boolean eraseCredentialsAfterAuthentication;
    
        //注意AuthenticationProvider对象,Spring Security针对每一种认证(比如QQ登录、微信登录等),都封装了一个AuthenticationProvider对象。
        public ProviderManager(List<AuthenticationProvider> providers) {
            this(providers, (AuthenticationManager)null);
        }
    
        public ProviderManager(List<AuthenticationProvider> providers, AuthenticationManager parent) {
            this.eventPublisher = new ProviderManager.NullEventPublisher();
            this.providers = Collections.emptyList();
            this.messages = SpringSecurityMessageSource.getAccessor();
            this.eraseCredentialsAfterAuthentication = true;
            Assert.notNull(providers, "providers list cannot be null");
            this.providers = providers;
            this.parent = parent;
            this.checkState();
        }
        //认证逻辑
        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;
            boolean debug = logger.isDebugEnabled();
            Iterator var8 = this.getProviders().iterator();
           //循环所有的AuthenticationProvider,匹配当前认证类型    
            while(var8.hasNext()) {
                AuthenticationProvider provider = (AuthenticationProvider)var8.next();
                if (provider.supports(toTest)) {
                    if (debug) {
                        logger.debug("Authentication attempt using " + provider.getClass().getName());
                    }
    
                    try {
                        //找到对应的认证类型,就继续调用AuthenticationProvider对象完整认证
                        result = provider.authenticate(authentication);
                        if (result != null) {
                            this.copyDetails(authentication, result);
                            break;
                        }
                    } catch (InternalAuthenticationServiceException | AccountStatusException var13) {
                        this.prepareException(var13, authentication);
                        throw var13;
                    } catch (AuthenticationException var14) {
                        lastException = var14;
                    }
                }
            }
    
            if (result == null && this.parent != null) {
                try {
                    result = parentResult = this.parent.authenticate(authentication);
                } catch (ProviderNotFoundException var11) {
                } catch (AuthenticationException var12) {
                    parentException = var12;
                    lastException = var12;
                }
            }
    
            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;
            }
        }
    
        //其他略
    }
    
    • AuthenticationProvider的源码如下:
    //AuthenticationProvider是接口,那么找子类呗
    public interface AuthenticationProvider {
        Authentication authenticate(Authentication var1) throws AuthenticationException;
    
        boolean supports(Class<?> var1);
    }
    

    AuthenticationProvider的子类

    • AbstractUserDetailsAuthenticationProvider的源码如下:
    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 Authentication authenticate(Authentication authentication) throws AuthenticationException {
            Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication, () -> {
                return this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports", "Only UsernamePasswordAuthenticationToken is supported");
            });
            String username = authentication.getPrincipal() == null ? "NONE_PROVIDED" : authentication.getName();
            boolean cacheWasUsed = true;
    
            UserDetails user = this.userCache.getUserFromCache(username);
            if (user == null) {
                cacheWasUsed = false;
    
                try {
                    //获取UserDetails
                    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);
                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();
            }
            //调用了createSuccessAuthentication方法    
            return this.createSuccessAuthentication(principalToReturn, authentication, user);
        }
    
        protected Authentication createSuccessAuthentication(Object principal, Authentication authentication, UserDetails user) {
            //调用的是UsernamePasswordAuthenticationToken的三个参数的构造方法
            UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(principal, authentication.getCredentials(), this.authoritiesMapper.mapAuthorities(user.getAuthorities()));
            result.setDetails(authentication.getDetails());
            return result;
        }
    
        //retrieveUser是个抽象方法,看子类DaoAuthenticationProvider怎么实现的
        protected abstract UserDetails retrieveUser(String var1, UsernamePasswordAuthenticationToken var2) throws AuthenticationException;
        //其他略    
    }   
    
    • DaoAuthenticationProvider的源码如下:
    public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
        private static final String USER_NOT_FOUND_PASSWORD = "userNotFoundPassword";
        private PasswordEncoder passwordEncoder;
        private volatile String userNotFoundEncodedPassword;
        private UserDetailsService userDetailsService;
        private UserDetailsPasswordService userDetailsPasswordService;
    
        protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
            this.prepareTimingAttackProtection();
    
            try {
                //UserDetails是Spring Security自己的用户对象。
                //this.getUserDetailsService()其实就是得到一个UserDetailsService的一个实现略
                //loadUserByUsername里面是真正的认证逻辑
                //那么我们直接编写一个UserDetailsService的实现类,告诉SpringSecurity就可以了。
                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);
            }
        }
    
        //其他略
    }    
    
    • 上面我已经知道自定义认证方法怎么写了,那么我们顺便看看返回的流程,查看AbstractUserDetailsAuthenticationProvider的authenticate方法后面的逻辑:
    public abstract class AbstractUserDetailsAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware {
    
        //其他略
    
        public Authentication authenticate(Authentication authentication) throws AuthenticationException {
            Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication, () -> {
    
            //调用了createSuccessAuthentication方法    
            return this.createSuccessAuthentication(principalToReturn, authentication, user);
        }
    
        protected Authentication createSuccessAuthentication(Object principal, Authentication authentication, UserDetails user) {
            //这边调用了UsernamePasswordAuthenticationToken三个参数的构造方法
            UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(principal, authentication.getCredentials(), this.authoritiesMapper.mapAuthorities(user.getAuthorities()));
            result.setDetails(authentication.getDetails());
            return result;
        }
    }    
    
    • UsernamePasswordAuthenticationToken的源码如下:
    public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationToken {
        private static final long serialVersionUID = 520L;
        private final Object principal;
        private Object credentials;
    
        //认证成功前,调用此方法
        public UsernamePasswordAuthenticationToken(Object principal, Object credentials) {
            super((Collection)null);
            this.principal = principal;
            this.credentials = credentials;
            this.setAuthenticated(false);
        }
    
        //认证成功后,调用此方法
        public UsernamePasswordAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
            //通过这个我们查父类,父类是AbstractAuthenticationToken
            super(authorities);
            this.principal = principal;
            this.credentials = credentials;
            super.setAuthenticated(true);
        }    
        //其他略
    }
    
    • AbstractAuthenticationToken的源码如下:
    public abstract class AbstractAuthenticationToken implements Authentication, CredentialsContainer {
        private final Collection<GrantedAuthority> authorities;
        private Object details;
        private boolean authenticated = false;
    
        public AbstractAuthenticationToken(Collection<? extends GrantedAuthority> authorities) {
            if (authorities == null) {
                this.authorities = AuthorityUtils.NO_AUTHORITIES;
            } else {
                Iterator var2 = authorities.iterator();
    
                GrantedAuthority a;
                do {
                    if (!var2.hasNext()) {
                        ArrayList<GrantedAuthority> temp = new ArrayList(authorities.size());
                        temp.addAll(authorities);
                        this.authorities = Collections.unmodifiableList(temp);
                        return;
                    }
    
                    a = (GrantedAuthority)var2.next();
                } while(a != null);
               //如果没有权限信息,会抛出异常    
                throw new IllegalArgumentException("Authorities collection cannot contain any null elements");
            }
        }
        //其他略
    }    
    

    在定义认证业务逻辑返回的UserDetails对象中一定要放置权限信息。

    • 回到UsernamePasswordAuthenticationFilter。这是个过滤器,但是都没有doFilter方法,那么我们从其父类AbstractAuthenticationProcessingFilter那边找doFilter方法。
    public abstract class AbstractAuthenticationProcessingFilter 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;
            if (!this.requiresAuthentication(request, response)) {
                chain.doFilter(request, response);
            } else {
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Request is to process authentication");
                }
    
                Authentication authResult;
                try {
                    //调用attemptAuthentication
                    authResult = this.attemptAuthentication(request, response);
                    if (authResult == null) {
                        return;
                    }
    
                    this.sessionStrategy.onAuthentication(authResult, request, response);
                } catch (InternalAuthenticationServiceException var8) {
                    this.logger.error("An internal error occurred while trying to authenticate the user.", var8);
                    this.unsuccessfulAuthentication(request, response, var8);
                    return;
                } catch (AuthenticationException var9) {
                    //失败,走unsuccessfulAuthentication方法
                    this.unsuccessfulAuthentication(request, response, var9);
                    return;
                }
    
                if (this.continueChainBeforeSuccessfulAuthentication) {
                    chain.doFilter(request, response);
                }
    		   //成功,走successfulAuthentication方法	
                this.successfulAuthentication(request, response, chain, authResult);
            }
        }
        
        //attemptAuthentication是抽象方法,由子类实现
        public abstract Authentication attemptAuthentication(HttpServletRequest var1, HttpServletResponse var2) throws AuthenticationException, IOException, ServletException;
    	
        //失败走unsuccessfulAuthentication
        protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
            SecurityContextHolder.clearContext();
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Authentication request failed: " + failed.toString(), failed);
                this.logger.debug("Updated SecurityContextHolder to contain null Authentication");
                this.logger.debug("Delegating to authentication failure handler " + this.failureHandler);
            }
    		
            this.rememberMeServices.loginFail(request, response);
            this.failureHandler.onAuthenticationFailure(request, response, failed);
        }
        
        //成功走successfulAuthentication
        protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Authentication success. Updating SecurityContextHolder to contain: " + authResult);
            }
    	    //认证成功,将认证信息存储到SecurityContext中
            SecurityContextHolder.getContext().setAuthentication(authResult);
            //登录成功,调用rememberMeServices的loginSuccess方法
            this.rememberMeServices.loginSuccess(request, response, authResult);
            if (this.eventPublisher != null) {
                this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
            }
    
            this.successHandler.onAuthenticationSuccess(request, response, authResult);
        }
        
        //其他略
    
    }
    
  • 相关阅读:
    208. Implement Trie (Prefix Tree)
    97. Interleaving String
    314. Binary Tree Vertical Order Traversal
    windows获取IP和MAC地址【Qt】
    阳历阴历转换
    getDat(char *val)获得某一天是这一年中的第几天
    int位数的获取及int类型转char *
    以二进制形式输出char *数据
    char类型变量二进制形式输出
    int类型变量以二进制形式输出
  • 原文地址:https://www.cnblogs.com/xuweiweiwoaini/p/13890551.html
Copyright © 2011-2022 走看看