(单体项目的认证,微服务项目的认证授权)
1.基本概念
1.1 什么是认证
进入移动互联网时代,大家每天都在刷手机,常用的软件有微信、支付宝、头条等,下边拿微信来举例子说明认证相关的基本概念,在初次使用微信前需要注册成为微信用户,然后输入账号和密码即可登录微信,输入账号和密码登录微信的过程就是认证。
系统为什么要认证?
认证是为了保护系统的隐私数据与资源,用户的身份合法方可访问该系统的资源。
认证︰用户认证就是判断一个用户的身份是否合法的过程,用户去访问系统资源时系统要求验证用户的身份信息,身份合法方可继续访问,不合法则拒绝访问。常见的用户身份认证方式有∶用户名密码登录,二维码登录,手机短信登录,指纹认证等方式。
认证是为了确保身份合法性,授权是在认证通过后发生的,控制不同的用户能够访问不同的资源。(这个框架 主要学认证和授权 .. . . .. )
1.2 什么是会话
用户认证通过后,为了避免用户的每次操作都进行认证可将用户的信息保证在会话中。会话就是系统为了保持当前用户的登录状态所提供的机制,常见的有基于session方式、基于token方式等。当我们进行支付的时候可能还会进行一次认证
基于session的认证:
交互流程是,用户认证成功后,在服务端生成用户相关的数据保存在session(当前会话)中,发给客户端的sesssion_id存放到cookie中,这样用户客户端请求时带上session_id就可以验证服务器端是否存在session数据,以此完成用户的合法校验,当用户退出系统或session过期销毁时,客户端的session_id也就无效了。(只是有存个id)
基于token的认证:
服务端可以不存放token 它可以通过生成token的那种方式去验证token。
1.3 授权的模型数据
这里我们需要了解一下RBAC
1.4 RBAC
1.4.1 基于角色的访问控制
RBAC基于角色的访问控制(Role-Based Access Control)是按角色进行授权,比如:主体的角色为总经理可以查 询企业运营报表,查询员工工资信息等,访问控制流程如下:
根据上图中的判断逻辑,授权代码可表示如下:
if(主体.hasRole("总经理角色id")){ 查询工资 }
如果上图中查询工资所需要的角色变化为总经理和部门经理,此时就需要修改判断逻辑为“判断用户的角色是否是 总经理或部门经理”,修改代码如下:
if(主体.hasRole("总经理角色id") || 主体.hasRole("部门经理角色id")){ 查询工资 }
可扩展性差。
1.4.2 基于资源的访问控制(推荐)
我只需要判断你这个角色有没有这个这个这个权限就行了。就不许要判断你是不是这个角色了,扩展性高
2 Spring Security
2.1 认证
2.1.1 认证页面
springSecurity默认提供认证页面,不需要额外开发。
2.1.2 安全配置 (很重要)
spring security提供了用户名密码登录、退出、会话管理等认证功能,只需要配置即可使用。
1) 在config包下定义WebSecurityConfig,安全配置的内容包括:用户信息、密码编码器、安全拦截机制。
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
//配置用户信息服务
@Bean
public UserDetailsService userDetailsService() {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
//这里我们说的是基于内存的从内存中找,从数据库中找也行。
manager.createUser(User.withUsername("zhangsan").password("123").authorities("p1").build());
manager.createUser(User.withUsername("lisi").password("456").authorities("p2").build());
return manager;
}
//密码编码器
@Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
//配置安全拦截机制
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/r/**").authenticated() (1)
.anyRequest().permitAll() (2)
.and()
.formLogin().successForwardUrl("/login‐success"); //运行表单提交 (3)
}
}
在userDetailsService()方法中,我们返回了一个UserDetailsService给spring容器,Spring Security会使用它来 获取用户信息。我们暂时使用InMemoryUserDetailsManager实现类,并在其中分别创建了zhangsan、lisi两个用 户,并设置密码和权限。 而在configure()中,我们通过HttpSecurity设置了安全拦截规则,其中包含了以下内容:
(1)url匹配/r/**的资源,经过认证后才能访问。
(2)其他url完全开放。
(3)支持form表单认证,认证成功后转向/login-success。
关于HttpSecurity的配置清单请参考附录 HttpSecurity。
2) 加载 WebSecurityConfig 修改SpringApplicationInitializer的getRootConfigClasses()方法,添加WebSecurityConfig.class
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { ApplicationConfig.class, WebSecurityConfig.class};
}
2.1.3 .Spring Security初始化
Spring Security初始化,这里有两种情况
若当前环境没有使用Spring或Spring MVC,则需要将 WebSecurityConfig(Spring Security配置类) 传入超 类,以确保获取配置,并创建spring context。
相反,若当前环境已经使用spring,我们应该在现有的springContext中注册Spring Security(上一步已经做将 WebSecurityConfig加载至rootcontext),此方法可以什么都不做。 在init包下定义SpringSecurityApplicationInitializer:
public class SpringSecurityApplicationInitializer
extends AbstractSecurityWebApplicationInitializer {
public SpringSecurityApplicationInitializer() {
//super(WebSecurityConfig.class);
}
}
在Spring框架中,
ApplicationInitializer
是一个接口,它用于配置和初始化Web应用程序上下文。具体来说,ApplicationInitializer
用于替代传统的web.xml文件,它允许您以编程方式配置Servlet、Filter和其他Web组件。通过实现
ApplicationInitializer
接口,您可以在应用程序启动时执行一些必要的初始化任务,例如:
配置和注册Servlet和Filter:您可以使用
ApplicationInitializer
来注册和配置Servlet和Filter,而无需使用web.xml文件。这样可以更灵活地管理您的Web组件。配置Spring上下文:
ApplicationInitializer
可以用于配置Spring应用程序上下文,包括加载和注册Bean定义,配置数据源、事务管理器等。配置安全性:您可以使用
ApplicationInitializer
来配置Spring Security,定义安全规则、身份验证和授权。配置其他Web组件:除了Servlet和Filter之外,您还可以使用
ApplicationInitializer
来配置其他Web组件,如监听器、拦截器等。使用
ApplicationInitializer
的主要好处是它提供了更灵活、可扩展的方式来配置和初始化Web应用程序。相比传统的web.xml文件,它使得配置更加集中和可读性更高。此外,ApplicationInitializer
还可以与其他Spring框架的功能集成,例如Spring MVC、Spring Security等。请注意,
ApplicationInitializer
是一个接口,您需要实现它并提供相应的初始化逻辑。在Spring中,AbstractAnnotationConfigDispatcherServletInitializer
和AbstractDispatcherServletInitializer
是两个提供了一些便捷方法的抽象类,您可以继承它们来简化ApplicationInitializer
的实现。
2.1.3.默认根路径请求
在WebConfig.java中添加默认请求根路径跳转到/login,此url为spring security
//默认Url根路径跳转到/login,此url为spring security提供
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("redirect:/login");
}
spring security默认提供的登录页面
2.1.4.认证成功页面
2.2 授权
实现授权需要对用户的访问进行拦截校验,校验用户的权限是否可以操作指定的资源,Spring Security默认提供授 权实现方法。
在LoginController添加/r/r1或/r/r2
/**
* 测试资源1
* @return
*/
@GetMapping(value = "/r/r1",produces = {"text/plain;charset=UTF‐8"})
public String r1(){
return " 访问资源1";
}
/**
* 测试资源2
* @return
*/
@GetMapping(value = "/r/r2",produces = {"text/plain;charset=UTF‐8"})
public String r2(){
return " 访问资源2";
}
在安全配置类WebSecurityConfig.java中配置授权规则:
.antMatchers("/r/r1").hasAuthority("p1")
.antMatchers("/r/r2").hasAuthority("p2")
.antMatchers("/r/r1").hasAuthority("p1")表示:访问/r/r1资源的 url需要拥有p1权限。 .antMatchers("/r/r2").hasAuthority("p2")表示:访问/r/r2资源的 url需要拥有p2权限。
完整的WebSecurityConfig方法如下:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/r/r1").hasAuthority("p1")
.antMatchers("/r/r2").hasAuthority("p2")
.antMatchers("/r/**").authenticated()
.anyRequest().permitAll()
.and()
.formLogin().successForwardUrl("/login‐success");
}
spring Securty 还有其他的功能。
3. springboot集成
自己去晚上搜索,不多bb
4 工作原理
Spring Security所解决的问题就是安全访问控制,而安全访问控制功能其实就是对所有进入系统的请求进行拦截, 校验每个请求是否能够访问它所期望的资源。其实可以通过Filter或AOP等技术来实现,Spring Security对Web资源的保护是靠Filter实现的,所以从这个Filter来入手,逐步深入Spring Security原理。
当初始化Spring Security时,会创建一个名为 SpringSecurityFilterChain 的Servlet过滤器,类型为 org.springframework.security.web.FilterChainProxy,它实现了javax.servlet.Filter,因此外部的请求会经过此 类,下图是Spring Security过虑器链结构图:
FilterChainProxy是一个代理,真正起作用的是FilterChainProxy中SecurityFilterChain所包含的各个Filter,同时 这些Filter作为Bean被Spring管理,它们是Spring Security核心,各有各的职责,但他们并不直接处理用户的认 证,也不直接处理用户的授权,而是把它们交给了认证管理器(AuthenticationManager)和决策管理器 (AccessDecisionManager)进行处理。
Spring Security功能的实现主要是由一系列过滤器链相互配合完成
下面介绍过滤器链中主要的几个过滤器及其作用:
SecurityContextPersistenceFilter 这个Filter是整个拦截过程的入口和出口(也就是第一个和最后一个拦截 器),会在请求开始时从配置好的 SecurityContextRepository 中获取 SecurityContext,然后把它设置给 SecurityContextHolder。在请求完成后将 SecurityContextHolder 持有的 SecurityContext 再保存到配置好的SecurityContextRepository,同时清除 securityContextHolder 所持有的 SecurityContext;
UsernamePasswordAuthenticationFilter 用于处理来自表单提交的认证。该表单必须提供对应的用户名和密 码,其内部还有登录成功或失败后进行处理的 AuthenticationSuccessHandler 和 AuthenticationFailureHandler,这些都可以根据需求做相关改变;
FilterSecurityInterceptor 是用于保护web资源的,使用AccessDecisionManager对当前用户进行授权访问,前 面已经详细介绍过了;
ExceptionTranslationFilter 能够捕获来自 FilterChain 所有的异常,并进行处理。但是它只会处理两类异常: AuthenticationException 和 AccessDeniedException,其它的异常它会继续抛出。