SpringSecurity 流程图
先来看下面一个 Spring Security 执行流程图,只要把 SpringSecurity 的执行过程弄明白了,这个框架就会变得很简单:
流程说明
-
客户端发起一个请求,进入 Security 过滤器链。
-
当到 LogoutFilter 的时候判断是否是登出路径,如果是登出路径则到 logoutHandler ,如果登出成功则到 logoutSuccessHandler 登出成功处理,如果登出失败则由 ExceptionTranslationFilter ;如果不是登出路径则直接进入下一个过滤器。
-
当到 UsernamePasswordAuthenticationFilter 的时候判断是否为登录路径,如果是,则进入该过滤器进行登录操作,如果登录失败则到 AuthenticationFailureHandler 登录失败处理器处理,如果登录成功则到 AuthenticationSuccessHandler 登录成功处理器处理,如果不是登录请求则不进入该过滤器。
-
当到 FilterSecurityInterceptor 的时候会拿到 uri ,根据 uri 去找对应的鉴权管理器,鉴权管理器做鉴权工作,鉴权成功则到 Controller 层否则到 AccessDeniedHandler 鉴权失败处理器处理。
Security 配置
===========
在 WebSecurityConfigurerAdapter
这个类里面可以完成上述流程图的所有配置
配置类伪代码
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailService).passwordEncoder(new BCryptPasswordEncoder());
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(“/resources//*.html", "/resources//*.js”);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin()
.loginPage(“/login_page”)
.passwordParameter(“username”)
.passwordParameter(“password”)
.loginProcessingUrl(“/sign_in”)
.permitAll()
.and().authorizeRequests().antMatchers(“/test”).hasRole(“test”)
.anyRequest().authenticated().accessDecisionManager(accessDecisionManager())
.and().logout().logoutSuccessHandler(new MyLogoutSuccessHandler())
.and().csrf().disable();
http.addFilterAt(getAuthenticationFilter(),UsernamePasswordAuthenticationFilter.class);
http.exceptionHandling().accessDeniedHandler(new MyAccessDeniedHandler());
http.addFilterAfter(new MyFittler(), LogoutFilter.class);
}
}
配置简介
- configure(AuthenticationManagerBuilder auth)
AuthenticationManager 的建造器,配置 AuthenticationManagerBuilder 会让Security 自动构建一个 AuthenticationManager(该类的功能参考流程图);如果想要使用该功能你需要配置一个 UserDetailService 和 PasswordEncoder。UserDetailsService 用于在认证器中根据用户传过来的用户名查找一个用户, PasswordEncoder 用于密码的加密与比对,我们存储用户密码的时候用PasswordEncoder.encode() 加密存储,在认证器里会调用 PasswordEncoder.matches() 方法进行密码比对。如果重写了该方法,Security 会启用 DaoAuthenticationProvider 这个认证器,该认证就是先调用 UserDetailsService.loadUserByUsername 然后使用 PasswordEncoder.matches() 进行密码比对,如果认证成功成功则返回一个 Authentication 对象。
- configure(WebSecurity web)
这个配置方法用于配置静态资源的处理方式,可使用 Ant 匹配规则。
- configure(HttpSecurity http)
这个配置方法是最关键的方法,也是最复杂的方法。我们慢慢掰开来说:
http
.formLogin()
.loginPage(“/login_page”)
.passwordParameter(“username”)
.passwordParameter(“password”)
.loginProcessingUrl(“/sign_in”)
.permitAll()
这是配置登录相关的操作从方法名可知,配置了登录页请求路径,密码属性名,用户名属性名,和登录请求路径,permitAll()代表任意用户可访问。
http
.authorizeRequests()
.antMatchers(“/test”).hasRole(“test”)
.anyRequest().authenticated()
.accessDecisionManager(accessDecisionManager());
以上配置是权限相关的配置,配置了一个 /test
url 该有什么权限才能访问, anyRequest() 表示所有请求,authenticated() 表示已登录用户才能访问, accessDecisionManager() 表示绑定在 url 上的鉴权管理器
为了对比,现在贴出另一个权限配置清单:
http.authorizeRequests()
.antMatchers(“/tets_a/“,”/test_b/”).hasRole(“test”)
.antMatchers(“/a/“,”/b/”).authenticated()
.accessDecisionManager(accessDecisionManager())
我们可以看到权限配置的自由度很高,鉴权管理器可以绑定到任意 url 上;而且可以硬编码各种 url 权限:
http
.logout()
.logoutUrl(“/logout”)
.logoutSuccessHandler(new MyLogoutSuccessHandler())
登出相关配置,这里配置了登出 url 和登出成功处理器:
http
.exceptionHandling()
.accessDeniedHandler(new MyAccessDeniedHandler());
上面代码是配置鉴权失败的处理器。
http.addFilterAfter(new MyFittler(), LogoutFilter.class);
http.addFilterAt(getAuthenticationFilter(),UsernamePasswordAuthenticationFilter.class);
上面代码展示如何在过滤器链中插入自己的过滤器,addFilterBefore 加在对应的过滤器之前,addFilterAfter 加在对应的过滤器之后,addFilterAt 加在过滤器同一位置,事实上框架原有的 Filter 在启动 HttpSecurity 配置的过程中,都由框架完成了其一定程度上固定的配置,是不允许更改替换的。根据测试结果来看,调用 addFilterAt 方法插入的 Filter ,会在这个位置上的原有 Filter 之前执行。
注:关于 HttpSecurity 使用的是链式编程,其中 http.xxxx.and.yyyyy
这种写法和 http.xxxx;http.yyyy
写法意义一样。
- 自定义 AuthenticationManager 和 AccessDecisionManager
重写 authenticationManagerBean() 方法,并构造一个 authenticationManager:
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
ProviderManager authenticationManager = new ProviderManager(Arrays.asLis(getMyAuthenticationProvider(),daoAuthenticationProvider()));
return authenticationManager;
}
我这里给 authenticationManager 配置了两个认证器,执行过程参考流程图。
定义构造AccessDecisionManager的方法并在配置类中调用,配置参考 configure(HttpSecurity http) 说明:
public AccessDecisionManager accessDecisionManager(){
List<AccessDecisionVoter<? extends Object>> decisionVoters
= Arrays.asList(
new MyExpressionVoter(),
new WebExpressionVoter(),
new RoleVoter(),
new AuthenticatedVoter());
return new UnanimousBased(decisionVoters);
}
投票管理器会收集投票器投票结果做统计,最终结果大于等于0代表通过;每个投票器会返回三个结果:-1(反对),0(通过),1(赞成)。
Security 权限系统
=============
- UserDetails
Security 中的用户接口,我们自定义用户类要实现该接口。
- GrantedAuthority
Security 中的用户权限接口,自定义权限需要实现该接口:
public class MyGrantedAuthority implements GrantedAuthority {
private String authority;
}
authority 表示权限字段,需要注意的是在 config 中配置的权限会被加上 ROLE_
前缀,比如我们的配置 authorizeRequests().antMatchers("/test").hasRole("test")
,配置了一个 test
权限但我们存储的权限字段(authority)应该是 ROLE_test
。
- UserDetailsService
Security 中的用户 Service,自定义用户服务类需要实现该接口:
@Service
public class MyUserDetailService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
return…
}
}
loadUserByUsername的作用在上文中已经说明,就是根据用户名查询用户对象。
- SecurityContextHolder
用户在完成登录后 Security 会将用户信息存储到这个类中,之后其他流程需要得到用户信息时都是从这个类中获得,用户信息被封装成 SecurityContext ,而实际存储的类是 SecurityContextHolderStrategy ,默认的SecurityContextHolderStrategy 实现类是 ThreadLocalSecurityContextHolderStrategy 它使用了ThreadLocal来存储了用户信息。
手动填充 SecurityContextHolder 示例:
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(“test”,“test”,list);
SecurityContextHolder.getContext().setAuthentication(token);
对于使用 token 鉴权的系统,我们就可以验证token后手动填充SecurityContextHolder,填充时机只要在执行投票器之前即可,或者干脆可以在投票器中填充,然后在登出操作中清空SecurityContextHolder。
Security 扩展
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
最后
给大家送一个小福利
附高清脑图,高清知识点讲解教程,以及一些面试真题及答案解析。送给需要的提升技术、准备面试跳槽、自身职业规划迷茫的朋友们。
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-KBbJhxQb-1712808942483)]
最后
给大家送一个小福利
[外链图片转存中…(img-VcUxelkI-1712808942483)]
附高清脑图,高清知识点讲解教程,以及一些面试真题及答案解析。送给需要的提升技术、准备面试跳槽、自身职业规划迷茫的朋友们。
[外链图片转存中…(img-QooxcPUf-1712808942483)]
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-1ukgAEvZ-1712808942484)]