内容简介:在前面两节认证工作流程:具体流程请看下面小节。
在前面两节 Spring security (一)架构框架-Component、Service、Filter分析 和 Spring Security(二)--WebSecurityConfigurer配置以及filter顺序 为Spring Security认证作好了准备,可以让我们更好的理解认证过程以及项目代码编写。
1.认证过程工作流程
认证工作流程:
AbstractAuthenticationProcessingFilter
doFilter()(attemptAuthentication()获取Authentication实体)
->UsernamePasswordAuthenticationFilter(AbstractAuthenticationProcessingFilter的子类)
attemptAuthentication() (在UsernamePasswordAuthenticationToken()中将username 和 password 生成 UsernamePasswordAuthenticationToken对象,getAuthenticationManager().authenticate进行认证以及返回获取Authentication实体)
->AuthenticationManager
->ProviderManager()(AuthenticationManager接口实现)
authenticate()(AuthenticationProvider.authenticate()进行认证并获取Authentication实体)
->AbstractUserDetailsAuthenticationProvider(内置缓存机制,如果缓存中没有用户信息就调用retrieveUser()获取用户)
authenticate() (获取Authentication实体需要userDetails,在缓存中或者retrieveUser()获取userDetails;验证additionalAuthenticationChecks(); createSuccessAuthentication()生成Authentication实体)
->DaoAuthenticationProvider
retrieveUser() (调用自定义UserDetailsService中loadUserByUsername()加载userDetails)
->UserDetailsService
loadUserByUsername()(获取userDetails)
复制代码
具体流程请看下面小节。
1.1:请求首先经过过滤器AbstractAuthenticationProcessingFilter以及UsernamePasswordAuthenticationFilter进行处理
当请求来临时,在默认情况下,请求先经过AbstractAuthenticationProcessingFilter的子类UsernamePasswordAuthenticationFilter过滤器。在UsernamePasswordAuthenticationFilter过滤器调用attemptAuthentication()方法现实主要的两步过程:
- 创建拥有用户的详情信息的Authentication对象,在默认的UsernamePasswordAuthenticationFilter中将创建UsernamePasswordAuthenticationToken的Authentication对象;
- AuthenticationManager调用authenticate()方法进行认证过程,在默认情况,使用ProviderManager类进行认证。
UsernamePasswordAuthenticationFilter源码分析:
public class UsernamePasswordAuthenticationFilter extends
AbstractAuthenticationProcessingFilter {
....
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
.....
//1.创建拥有用户的详情信息的Authentication对象
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
//2.AuthenticationManager进行认证
return this.getAuthenticationManager().authenticate(authRequest);
}
...
}
复制代码
1.2请求经过过滤器处理之后,在AuthenticationManager以及ProviderManager认证
在UsernamePasswordAuthenticationFilter中看出,将调用AuthenticationManager接口的authenticate()方法进行详细认证。默认情况将使用AuthenticationManager子类ProviderManager的authenticate()进行认证,可以分成三个主要过程:
- AuthenticationProvide.authenticate()进行认证,默认下,将使用AbstractUserDetailsAuthenticationProvider进行认证;
- 认证成功后,从authentication中删除凭据和其他机密数据,否则抛出异常或者认证失败;
- 发布认证成功事件,并将Authentication对象保存到security context中。
ProviderManager源码分析:
public class ProviderManager implements AuthenticationManager, MessageSourceAware,
InitializingBean {
...
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
...
//AuthenticationProvider依次进行认证
for (AuthenticationProvider provider : getProviders()) {
...
try {
//1.1进行认证,并返回Authentication对象
result = provider.authenticate(authentication);
if (result != null) {
copyDetails(authentication, result);
break;
}
}
...
catch (AuthenticationException e) {
lastException = e;
}
}
if (result == null && parent != null) {
// Allow the parent to try.
try {
//1.2如果1.1认证中没有一个验证通过,则使用父类型AuthenticationManager进行验证
result = parent.authenticate(authentication);
}
catch (ProviderNotFoundException e) {
// ignore as we will throw below if no other exception occurred prior to
// calling parent and the parent
// may throw ProviderNotFound even though a provider in the child already
// handled the request
}
catch (AuthenticationException e) {
lastException = e;
}
}
//2.从authentication中删除凭据和其他机密数据
if (result != null) {
if (eraseCredentialsAfterAuthentication
&& (result instanceof CredentialsContainer)) {
// Authentication is complete. Remove credentials and other secret data
// from authentication
((CredentialsContainer) result).eraseCredentials();
}
//3.发布认证成功事件,并将Authentication对象保存到security context中
eventPublisher.publishAuthenticationSuccess(result);
return result;
}
}
复制代码
1.3 认证过程详细处理:AuthenticationProvider、AbstractUserDetailsAuthenticationProvider以及DaoAuthenticationProvider
在默认认证详细处理过程中,AuthenticationProvider认证由AbstractUserDetailsAuthenticationProvider抽象类以及AbstractUserDetailsAuthenticationProvider的子类DaoAuthenticationProvider进行方法重写协助共同工作进行认证的。主要可以分成以下步骤:
-
获取用户信息UserDetails,首先从缓存中读取信息,如果缓存中没有的化,在UserDetailsService中加载,其最主要可以从我们自定义的UserDetailsService进行读取用户信息UserDetails;
-
验证三步走:
1). preAuthenticationChecks
2). additionalAuthenticationChecks:使用PasswordEncoder.matches()方法进行认证,其验证方式中验证数据已经过PasswordEncoder算法加密,可以通过实现PasswordEncoder接口来定义算法加密方式。
3). postAuthenticationChecks
-
将已通过验证的用户信息封装成 UsernamePasswordAuthenticationToken对象并返回;该对象封装了用户的身份信息,以及相应的权限信息。
AbstractUserDetailsAuthenticationProvider主要功能提供authenticate()认证方法以及给DaoAuthenticationProvider重写方法源码分析:
public abstract class AbstractUserDetailsAuthenticationProvider implements
AuthenticationProvider, InitializingBean, MessageSourceAware {
...
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
...
boolean cacheWasUsed = true;
//1.1获取缓存中UserDetails信息
UserDetails user = this.userCache.getUserFromCache(username);
//1.2 如果缓存中没有信息,从UserDetailsService中获取
if (user == null) {
cacheWasUsed = false;
try {
//使用DaoAuthenticationProvider中重写的方法去获取信息
user = retrieveUser(username,
(UsernamePasswordAuthenticationToken) authentication);
}catch{
...
}
...
try {
//进行检验认证
preAuthenticationChecks.check(user);
additionalAuthenticationChecks(user,
(UsernamePasswordAuthenticationToken) authentication);
}catch{
...
}
...
postAuthenticationChecks.check(user);
....
// 将已通过验证的用户信息封装成 UsernamePasswordAuthenticationToken对象并返回
return createSuccessAuthentication(principalToReturn, authentication, user);
}
复制代码
DaoAuthenticationProvider功能主要为认证凭证加密PasswordEncoder,以及重写AbstractUserDetailsAuthenticationProvider抽象类的retrieveUser、additionalAuthenticationChecks方法,其中retrieveUser主要是获取UserDetails信息,源码分析
protected final UserDetails retrieveUser(String username,
UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
prepareTimingAttackProtection();
try {
//根据UserDetailsService获取UserDetails信息,从自定义的UserDetailsService获取
UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
if (loadedUser == null) {
throw new InternalAuthenticationServiceException(
"UserDetailsService returned null, which is an interface contract violation");
}
return loadedUser;
}
catch (UsernameNotFoundException ex) {
mitigateAgainstTimingAttack(authentication);
throw ex;
}
catch (InternalAuthenticationServiceException ex) {
throw ex;
}
catch (Exception ex) {
throw new InternalAuthenticationServiceException(ex.getMessage(), ex);
}
}
复制代码
additionalAuthenticationChecks主要使用PasswordEncoder进行密码验证,源码分析:
protected void additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
if (authentication.getCredentials() == null) {
logger.debug("Authentication failed: no credentials provided");
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials",
"Bad credentials"));
}
String presentedPassword = authentication.getCredentials().toString();
//进行密码验证
if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
logger.debug("Authentication failed: password does not match stored value");
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials",
"Bad credentials"));
}
}
复制代码
1.4 认证中所需的认证凭证获取:UserDetailsService
在认证中必须获取认证凭证,从UserDetailsService获取到认证凭证,UserDetailsService接口只有一个方法:
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException; 复制代码
通过用户名 username 调用方法 loadUserByUsername 返回了一个UserDetails接口对象:
public interface UserDetails extends Serializable {
//1.权限集合
Collection<? extends GrantedAuthority> getAuthorities();
//2.密码
String getPassword();
//3.用户名
String getUsername();
//4.用户是否过期
boolean isAccountNonExpired();
//5.是否锁定
boolean isAccountNonLocked();
//6.用户密码是否过期
boolean isCredentialsNonExpired();
//7.账号是否可用(可理解为是否删除)
boolean isEnabled();
}
复制代码
我们通过实现UserDetailsService自定义获取UserDetails类,可以从不同数据源中获取认证凭证。
1.5 总结
总结 Spring Security(二)--WebSecurityConfigurer配置以及filter顺序 和本节Spring security(三)想要实现简单认证过程:
- 第一步:配置WebSecurityConfig
- 第二步: 实现自定义UserDetailsService,自定义从数据源码获取认证凭证。
2 Spring boot与Spring security整合
2.1配置WebSecurityConfig
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// TODO Auto-generated method stub
//super.configure(http);
http .csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/login/form")
.failureUrl("/login-error")
.permitAll() //表单登录,permitAll()表示这个不需要验证 登录页面,登录失败页面
.and()
.logout();
}
}
复制代码
2.2 UserDetailsService实现
@service
public class MyUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// TODO Auto-generated method stub
//这里可以可以通过username(登录时输入的用户名)然后到数据库中找到对应的用户信息,并构建成我们自己的UserInfo来返回。
return null;
}
}
// TODO Auto-generated method stub
//这里可以通过数据库来查找到实际的用户信息,这里我们先模拟下,后续我们用数据库来实现
if(username.equals("admin"))
{
//假设返回的用户信息如下;
UserInfo userInfo=new UserInfo("admin", "123456", "ROLE_ADMIN", true,true,true, true);
return userInfo;
}
return null;
}
复制代码
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Twenty Lectures on Algorithmic Game Theory
Tim Roughgarden / Cambridge University Press / 2016-8-31 / USD 34.99
Computer science and economics have engaged in a lively interaction over the past fifteen years, resulting in the new field of algorithmic game theory. Many problems that are central to modern compute......一起来看看 《Twenty Lectures on Algorithmic Game Theory》 这本书的介绍吧!