• 背景介绍: 在认识Spring Security之前,所有的权限验证逻辑都混杂在业务逻辑中,用户的每个操作以前可能都需要对用户是否有进行该项操作的权限进行判断,来达到认证授权的目的。类似这样的权限验证逻辑代码被分散在系统的许多地方,难以维护。 spring security的简单原理: 其实spring security这样的权限框架就是根据一系列的依赖代理(delegates)和servlet过滤器来实现的。 Spring security使用众多的拦截器对url拦截,以此来管理权限。大致流程:
  • 用户信息—>数据库: 根据请求的用户名加载对应的UserDetails,然后比对UserDetails的密码与认证请求的密码是否一致,一致则表示认证通过。Spring Security内部的DaoAuthenticationProvider就是使用的这种方式。其内部使用UserDetailsService来负责加载UserDetails,在认证成功以后会使用加载的UserDetails来封装要返回的Authentication对象,加载的UserDetails对象是包含用户权限等信息的。认证成功返回的Authentication对象将会保存在当前的SecurityContext中。通过Authentication.getPrincipal()返回一个UserDetails的实例。Spring Security内部使用的UserDetails实现类大都是内置的User类。 可以自定义UserDetails,在该实现类中我们可以定义一些获取用户其它信息的方法,这样就可以直接从当前SecurityContext的Authentication的principal中获取自定义的信息了。
  • 这里是列表文本SecurityContextHolder:用来保存SecurityContext **Q:**既然SecurityContext 是存放在 ThreadLocal 中的,而且在每次权限鉴定的时候都是从 ThreadLocal 中获取 SecurityContext 中对应的 Authentication 所拥有的权限,并且不同的 request 是不同的线程,为什么每次都可以从 ThreadLocal 中获取到当前用户对应的 SecurityContext 呢? R: SecurityContextPersistentFilter: 在 Web 应用中这是通过 SecurityContextPersistentFilter 实现的,默认情况下其会在每次请求开始的时候从 session 中获取 SecurityContext,然后把它设置给 SecurityContextHolder,在请求结束后又会将 SecurityContextHolder 所持有的 SecurityContext 保存在 session 中,并且清除 SecurityContextHolder 所持有的 SecurityContext。这样当我们第一次访问系统的时候,SecurityContextHolder 所持有的 SecurityContext 肯定是空的,待我们登录成功后,SecurityContextHolder 所持有的 SecurityContext 就不是空的了,且包含有认证成功的 Authentication 对象,待请求结束后我们就会将 SecurityContext 存在 session 中,等到下次请求的时候就可以从 session 中获取到该 SecurityContext 并把它赋予给 SecurityContextHolder 了,由于 SecurityContextHolder 已经持有认证过的 Authentication 对象了,所以下次访问的时候也就不再需要进行登录认证了。
  • 这里是列表文本AbstractSecurityInterceptor FilterSecurityInterceptor, MethodSecurityInterceptor, AspectJSecurityInterceptor(JoinPoint) 自定义:从数据库中获取用户、权限、资源来实现权限管理 1) MySecurityFilter: 从数据库中获取权限验证的数据,所以要重写FilterInvocationSecurityMetadataSource 接口, AccessDecisionManager 接口,UserDetailsService 接口。项目启动后,进行资源请求时,就自动会去执行我们重定义的类进行权限判断。 a. MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource (资源数据定义) 加载所有url和权限的对应关系。 b. MyAccessDecisionManager implements AccessDecisionManager 接口(过滤用户请求) 参数说明: 1、configAttributes 装载了请求的url允许的角色数组 。这里是从MySecurityMetadataSource里的loadResourceDefine方法里的atts对象取出的角色数据赋予给了configAttributes对象 2、authentication 装载了从数据库读出来的角色数据。这里是从MyUserDetailsService里的loadUserByUsername方法里的auths对象的值传过来给 authentication 对象 [AbstractSecurityInterceptor有三个派生类 FilterSecurityInterceptor,负责处理FilterInvocation,实现对URL资源的拦截 MethodSecurityInterceptor,负责处理MethodInvocation,实现对方法调用的拦截 AspectJSecurityInterceptor,负责处理JoinPoint,主要是用于对切面方法(AOP)调用的拦截 还可以直接使用注解对Action方法进行拦截,例如在方法上加 @PreAuthorize("hasRole('ROLE_SUPER')");负责权限的控制,如果请求的url在权限集合中有这个url对应的值,则放行。注:如果数据库中没有对这个url定义访问的权限,默认是会被放行的] c. 认证管理器AuthenticationManager(用户认证的入口)—>UserDetailsService接口、 MyUserDetailServiceImpl:【登录验证+获取用户权限】
  •