SpringSecurity(一)表单登录之登录认证
springsecurity是spring推荐的安全权限框架,今天我们来看看它的原理,后面会放github地址
springsecurity包含了两部分:登录认证和访问授权
1.登录认证
–首先我们来看看表单登录,表单登录需要在security配置类里面配置HttpSecurity 的 formLogin
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
.authorizeRequests()
// 首页和登录页面
.antMatchers("/").permitAll()
// 其他所有请求需要身份认证
.anyRequest().authenticated()
// 配置登录认证
.and().formLogin().loginProcessingUrl("/login");
}
}
不配置HttpSecurity 的 formLogin也可以,可以在配置里再加上最后一句,在
JwtLoginFilter里面继承UsernamePasswordAuthenticationFilter也可以
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
.authorizeRequests()
// 首页和登录页面
.antMatchers("/").permitAll()
// 其他所有请求需要身份认证
.anyRequest().authenticated()
// 配置登录认证
.and().formLogin().loginProcessingUrl("/login");
// 开启登录认证流程过滤器
http.addFilterBefore(new JwtLoginFilter(authenticationManager()), UsernamePasswordAuthenticationFilter.class);
}
}
为什么表单登录要在配置类配置formLogin呢?
不要急,我们来看看formLogin里面做了哪些操作:
点进去我们会看到这一段代码
发现它会返回一个FormLoginConfigurer对象,我们点进去会看到
发现里面有绑定一个UsernamePasswordAuthenticationFilter过滤器,进去擦查看
如果你的请求地址是/login,并且为post请求,那么就会被这个过滤器所拦截,
这就是我们上面配置类的那两个方法为什么需要那样配置的原因。
接下来我们继续看被这个过滤器拦截之后做了哪些操作:
首先,UsernamePasswordAuthenticationFilter继承了AbstractAuthenticationProcessingFilter,父类里面的doFilter如图
建议大家跟着点击进去查看里面的相关逻辑,我们先来看里面的主要操作
1.authResult = attemptAuthentication(request, response);
此方法由其子类继承所实现,我们这里用的是表单登录,由UsernamePasswordAuthenticationFilter重写里面的逻辑,其他登录方式可以通过继承AbstractAuthenticationProcessingFilter重写实现具体逻辑。然后我们回到子类UsernamePasswordAuthenticationFilter里attemptAuthentication的相关逻辑:
重写这里可以自己定义token。
我们看最后一行代码
return this.getAuthenticationManager().authenticate(authRequest);
调用的是AuthenticationManager 的authenticate方法,AuthenticationManager 是一个接口,它有一个默认的实现类ProviderManager,而ProviderManager委托给了AuthenticationProvider的authenticate方法有许多繁衍出来的类
AbstractUserDetailsAuthenticationProvider是其中的一个实现类,定义了较为统一的验证逻辑,我们可以继承这个类完成各种验证。我们来看看AbstractUserDetailsAuthenticationProvider
public Authentication authenticate(Authentication authentication) throws AuthenticationException {// Determine username
String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED" : authentication.getName();
boolean cacheWasUsed = true;
UserDetails user = this.userCache.getUserFromCache(username);
if (user == null) {
cacheWasUsed = false;
try {
// 子类根据自身情况从指定的地方加载认证需要的用户信息
user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
}
...try {
// 前置检查,一般是检查账号状态,如是否锁定之类
preAuthenticationChecks.check(user);
// 进行一般逻辑认证,如 DaoAuthenticationProvider 实现中的密码验证就是在这里完成的
additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);
}
...
// 后置检查,如可以检查密码是否过期之类
postAuthenticationChecks.check(user);
...
// 验证成功之后返回包含完整认证信息的 Authentication 对象
return createSuccessAuthentication(principalToReturn, authentication, user);
}
它是利用retrieveUser来获取认证的
retrieveUser里面只有一个方法,loadUserByUsername(String username),一般需要我们实现此接口方法,根据用户名加载登录认证和访问授权所需要的信息,并返回一个 UserDetails的实现类,后面登录认证和访问授权都需要用到此中的信息。
2.successfulAuthentication
登陆成功后,将认证后的 Authentication 对象存储到请求线程上下文,这样在授权阶段就可以获取到 Authentication 认证信息,并利用 Authentication 内的权限信息进行访问控制判断
重写这里自己生成token,返回前端…
源码地址:
https://github.com/15915972008/start-admin