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,会出现如下的界面:
- 默认的用户名是: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;
}
//其他略
}
- 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);
}
- 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);
}
//其他略
}