提高网站安全性,河北邯郸邮政编码,内网网站建设方面政策,泉州做网站开发公司目录
提示
Apache Shiro和Spring Security
认证和授权
RBAC
Demo
环境
Controller
引入Spring Security
初探Security原理
认证授权图示编辑
图中涉及的类和接口
流程总结 提示 Spring Security源码的接口名和方法名都很长#xff0c;看源码的时候要见名知意看源码的时候要见名知意有必要细看接口名和方法名另外可以借助流程图调试追踪代码有助于理解学习 Apache Shiro和Spring Security
Spring Security是一个可高度可定制的身份验证认证和访问控制授权框架它是用于保护基于Spring的应用程序的实际标准相比与另外一个安全框架Shiro它提供了更丰富的功能社区资源也比Shiro丰富。 Shiro轻量级不依赖Spring是第三方框架简单而灵活可以用于非Web环境Security重量级依赖Spring控制粒度更细老版本不能脱离Web新版本可以Spring Boot 2默认使用Security 5要求JDK至少是8Spring Boot 3默认使用Security 6要求JDK至少是17Security 5和Security 6的区别之一就是5到6废弃了WebSecurityConfigurerAdapter类在Security 5编写配置类需要继承WebSecurityConfigurerAdapter并重写某些方法但是在Security 6已经不需要了 Spring Security升级已弃用的 WebSecurityConfigurerAdapter - spring 中文网 从 Spring Security 5 迁移到 Spring Security 6/Spring Boot 3 - spring 中文网 认证和授权 认证Authentication验证当前访问系统的是不是本系统的用户并且要确认具体是哪个用户 授权Authorization经过认证后判断当前用户是否有权限进行某个操作 注Authentication和Authorization拼写很像有必要分清后面看源代码方便 RBAC
Role-Based Access Control 基于角色的访问控制 把权限打包给角色角色拥有一组权限给用户分配角色用户拥有多个角色 最少包括五张表 用户表、角色表、用户角色表、权限表、角色权限表 Demo
环境 Spring Boot2.7.2 SpringSecurity5 JDK1.8 Controller
RestController
RequestMapping(/admin)
public class AdminController {GetMapping(/query)public String queryInfo(){return I am a admin;}
} 可以直接访问 引入Spring Security
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-security/artifactId
/dependency再次访问时默认出现了该登陆界面默认用户是user密码默认在控制台生成 成功登陆后才会放行。Spring Security默认拦截了除登录、退出之外的所有请求。显示的登录和退出表单是Security默认生成的。
前端页面样式bootstrap.min.css是一个CDN地址需要魔法所以加载很慢 初探Security原理
Spring Security底层是基于Servlet的过滤器链默认共有16个过滤器这里面我们只需要重点关注两个过滤器即可UsernamePasswordAuthenticationFilter负责登录认证FilterSecurityInterceptor负责权限授权。 认证授权图示 图中涉及的类和接口 UsernamePasswordAuthenticationFilter类 抽象类AbstractAuthenticationProcessingFilter的子类是常用的用户名和密码认证方式的主要处理类,该类中将用请求信息封装为初步的AuthenticationAuthentication实际上是一个接口它的实现类是UsernamePasswordAuthenticationToken此时最多只有用户名和密码在登录认证成功之后又会生成一个包含用户权限等信息的更全面的 Authentication 对象然后把它保存在 SecurityContextHolder 所持有的 SecurityContext 中。 AuthenticationManager接口 public interface AuthenticationManager {Authentication authenticate(Authentication authentication) throws AuthenticationException;
} 用来处理Authentication请求的接口。在其中只定义了一个方法 authenticate()该方法只接收一个代表认证请求的 Authentication 对象作为参数如果认证成功则会返回一个封装了当前用户权限等信息的 Authentication 对象。 ProviderManager类 public class ProviderManager implements AuthenticationManager, MessageSourceAware, InitializingBean AuthenticationManager接口的实现类AuthenticationManager接口不直接自己处理认证请求而是委托给ProviderManager所配置的AuthenticationProvider 列表而ProviderManager的作用就是管理这个AuthenticationProvider列表管理的方式是通过for循环去遍历该列表因为不同的登录逻辑表单登录、qq登录、邮箱登录是不一样的那么AuthenticationProvider列表就要支持不同的Authentication。 信息验证的逻辑都是在AuthenticationProvider里面会依次使用每一个 AuthenticationProvider 进行认证如果有一个 AuthenticationProvider 认证后的结果不为 null则表示该 AuthenticationProvider 已经认证成功之后的 AuthenticationProvider 将不再继续认证。然后直接以该 AuthenticationProvider 的认证结果作为 ProviderManager 的认证结果。如果所有的 AuthenticationProvider 的认证结果都为 null则表示认证失败将抛出一个 ProviderNotFoundException。 AuthenticationProvider接口该接口被实现为抽象类AbstractUserDetailsAuthenticationProvider然后DaoAuthenticationProvider类继承该抽象类并拥有一个UserDetailsService的变量 public abstract class AbstractUserDetailsAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAwarepublic class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider UserDetailsService接口 public interface UserDetailsService {UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
} 加载用户数据的核心接口可以自定义从数据库加载数据或者从内存加载临时用户InMemory,登录认证的时候 Spring Security 会通过 UserDetailsService 的 loadUserByUsername() 方法获取对应的 UserDetails 类型的用户信息进行认证认证通过后会将用户信息封装为UserDetails接口的实现类User并赋给认证通过的 Authentication 的 principal然后再把该 Authentication 存入到 SecurityContext 中。 InMemoryUserDetailsManager类 该类是UserDetailsService接口的实现类作用是从内存中加载用户也就是说用户是在代码中提前写好的程序运行后被加载到内存不是从数据库中取的没有持久化如果是从数据库加载用户就不用这个类了。该类有一个方法是 private User createUserDetails(String name, UserAttribute attr)返回值是User类型 UserDetails接口 public interface UserDetails extends Serializable 提供用户核心信息的接口,通过 UserDetailsService 的 loadUserByUsername() 方法获取,然后将该 UserDetails 赋给认证通过的 Authentication 的 principal。 User类 public class User implements UserDetails, CredentialsContainer UserDetails接口的实现类User类也是security的默认用户类我们可以继承该类对其方法重写 SecurityContextHolder类 用来保存 SecurityContext 的SecurityContext 中含有当前正在访问系统的用户的详细信息。默认情况下SecurityContextHolder 使用 ThreadLocal 来保存 SecurityContext这也就意味着在处于同一线程中的方法中我们可以从 ThreadLocal 中获取到当前的 SecurityContext。因为线程池的原因如果我们每次在请求完成后都将 ThreadLocal 进行清除的话那么我们把 SecurityContext 存放在 ThreadLocal 中还是比较安全的。这些工作 Spring Security 已经自动为我们做了即在每一次 request 结束后都将清除当前线程的 ThreadLocal。 流程总结
用户填写的用户名密码传到后端进入Security的过滤器链进行验证验证流程为
进入UsernamePasswordAuthenticationFilter里面有一个attemptAuthentication方法该方法会生成一个UsernamePasswordAuthenticationToken也就是一个凭证Authentication这个Authentication只包含了用户名、密码这些基础信息没有权限等其他信息然后Authentication作为参数被传到AuthenticationManager接口的实现类ProviderManager类的authenticate方法进行认证ProviderManager类中有一个ListAuthenticationProvider 列表AuthenticationProvider是接口AbstractUserDetailsAuthenticationProvider是接口的抽象类DaoAuthenticationProvider是实现类该列表存放着不同的登录逻辑AuthenticationProvider通过for循环去遍历该列表信息验证的逻辑都是在AuthenticationProvider里面会依次使用每一AuthenticationProvider进行认证如果有一个 AuthenticationProvider 认证后的结果不为 null则表示该AuthenticationProvider已经认证成功之后的 AuthenticationProvider 将不再继续认证。然后直接以该 AuthenticationProvider 的认证结果作为 ProviderManager 的认证结果。如果所有的 AuthenticationProvider 的认证结果都为 null则表示认证失败将抛出一个 ProviderNotFoundException。
DaoAuthenticationProvider内部的认证逻辑它有一个retrieveUser方法该方法调用UserDetailsService().loadUserByUsername从数据库获取用户信息并返回一个UserDetails类型的对象它含有用户的详细信息如用户权限等等然后将UserDetails对象和Authentication校验成功后会把UserDetails中的信息填入到Authentication一个最终的Authentication就产生了它会被保存到安全上下文中