spring security登录认证授权
是什么
Spring Security 主要实现了Authentication(认证,解决who are you? ) 和 Access Control(访问控制,也就是what are you allowed to do?,也称为Authorization)。Spring Security在架构上将认证与授权分离,并提供了扩展点
使用-1 快速开始 引入依赖,出现登录页面
<!-- 实现对 Spring MVC 的自动化配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 实现对 Spring Security 的自动化配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
自定义登录的用户名密码(练手)
配置类继承适配器 WebSecurityConfigurerAdapter, 重写configure方法,实现UserDetailsService接口,设置用户名,密码
从数据库读取密码
配置类继承适配器 WebSecurityConfigurerAdapter, 重写configure方法,实现UserDetailsService接口,调用UserDetailsService的已有的实现类,或者调用自定义的类,实现了UserDetailsService实现类接口,重写loadUserByUsername方法(数据库查登录的用户名密码)
认证逻辑
认证逻辑:SecurityContextPersistenceFilter Security上下文持久化过滤器拿到登录信息,生成session存储 ->SecurityContextHolderStrategy 策略 ->ThreadLocalSecurityContextHolderStrategy 用的ThreadLocal
AbstractAuthenticationProcessingFilter 的 doFilter方法->attemptAuthentication方法->AuthenticationManager接口的authenticate接口方法
->retrieveUser->getUserDetailsService().loadUserByUsername(获取到自定义的数据库获取用户名密码方法)–>authenticate方法中 调用additionalAuthenticationChecks
检查用户名密码的准确性–>正确的话就走认证成功的逻辑,认证成功后 ->SecurityContextHolder.getContext().setAuthentication设置登录信息到上下文;错误就走认真失败的逻辑
自定义认证成功失败的处理
自定义登录成功失败逻辑(执行方法步骤)原理:
认证成功后: successForwardUrl–>ForwardAuthenticationSuccessHandler->调用了AuthenticationSuccessHandler 的onAuthenticationSuccess 方法
因此可以实现AuthenticationSuccessHandler接口,重写 onAuthenticationSuccess 方法
认证失败同理:failureForwardUrl->ForwardAuthenticationFailureHandler->调用了 ForwardAuthenticationFailureHandler的 onAuthenticationFailure 方法
因此可以实现 ForwardAuthenticationFailureHandler接口,重写 onAuthenticationFailure 方法
会话管理(session)
是什么?http请求是无状态的,因此需要在浏览器和服务器端存储状态,即会话。
tomcat 会生成session对象,响应的时候会带给客户端存到浏览器的Cookie中,
下次再请求服务器时,带上JSESSIONID,和服务器的JSESSIONID比对,相同的话响应,否则不响应
原理 :SecurityContextHolder.getContext().getAuthentication()可以获取到登录信息,因为登录的认证信息存在 SecurityContext上下文中
AbstractAuthenticationProcessingFilter 的 doFilter方法 -> successfulAuthentication 认证成功后 ->SecurityContextHolder.getContext().setAuthentication 登录的认证信息存在 SecurityContext上下文中
会话并发控制-spring security实现
同一个账号只能同时登录一次(后面登录人把前面登录人挤掉)—>可以用redis实现,此处是用spring security实现
http.sessionManagement().maximumSessions(1)
原理:AbstractAuthenticationProcessingFilter -> sessionStrategy.onAuthentication()-> ConcurrentSessionControlAuthenticationStrategy 的onAuthentication方法比较当前登录的session个数是否小于我们自己设置 -> 是的话就session 设置过期- >ConcurrentSessionFilter 并发session过滤器 -> doFilter方法 -> 没有过期则登录成功,否则走session过期策略
可自定义实现SessionInformationExpiredStrategy接口重写onExpiredSessionDetected方法
问题:http.sessionManagement().maximumSessions(1).maxSessionsPreventsLogin(true) //超过最大的登录数,阻止登录
问题:我们在yml配置的http session设置的超时时间和内部SessionInformation 是重新的一个session,超时时间和之前不同,导致http session设置的超时时间超时了,但是SessionInformation 没超时会报多人同时登录的错误最好不要用,可以用redis实现
会话并发控制-redis实现
实现原理:引入的spring session包中有个session仓库的过滤器 SessionRepositoryFilter -> doFilter方法 ->commitSession ->sessionRepository.save(session)实现了重写了RedisSessionRepository中save 方法,把session存到redis中
1.引入spring session依赖
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.1.0</version>
</dependency>
2.修改application.yml
xmlsession:store-type: redisredis:host: localhostport: 6379
3.开启多个项目实例即可测试
Remember me实现
设置数据源,Spring Security会自动把用户信息存储到数据源中,下次登录直接比对数据库值就ok
原理:RememberMeAuthenticationFilter -> doFilter方法 -> autoLogin ->AbstractRememberMeServices的autoLogin ->PersistentTokenBasedRememberMeServices 的processAutoLoginCookie 取出remember-me的值拆分为series和token 去数据库根据series找到记录,看token是否一致一致就调用 getUserDetailsService().loadUserByUsername方法,实现自动登录
退出登录
原理:LogoutFilter-> doFilter方法 -> SecurityContextLogoutHandler的logout方法,setAuthentication为null清除认证状态,销毁HTTPSession对象
SimpleUrlLogoutSuccessHandler的onLogoutSuccess 重定向到登录页面
csrf跨站请求伪造
从 Spring Security4开始CSRF防护默认开启,默认会拦截请求,进行CSRF处理。CSRF为了保证不是其他第三方网站访问,要求访问时携带参数名为 _csrf 值为token(token 在服务端产生,在渲染请求页面时埋入页面)的内容,如果token和服务端的token匹配成功,则正常访问。
原理:CsrfFilter -> doFilter方法 -> 如果没有token 先生成一个,然后放到填充到CSRF_TOKEN中返回给页面,会再走一遍doFilter方法去比对CSRF_TOKEN和服务端的token是否能匹配成功,成功则可以访问,否则不能访问
授权
这里是引用访问控制url的匹配
antMatchers ant 表达式,统配符 ? 匹配任何单字符,* 匹配0或者任意数量的字符, ** 匹配0或者更多的目录
hasAuthority 有…的角色权限
hasAnyAuthority 有多个角色权限,符合一个即可
自定义403处理方案
使用 Spring Security 时经常会看见 403(无权限)。Spring Security 支持自定义权限受限处理,需要
实现 AccessDeniedHandler接口
基于方法的授权
1.JSR250注解,可以定义在方法和类上面
配置类开启@EnableGlobalMethodSecurity(jsr250Enabled = true,securedEnabled = true,prePostEnabled = true)
@RolesAllowed 访问对应方法时所应该具有的角色
@PermitAll 不进行权限控制
@DenyAll 无论什么角色都不能访问
2.@Secured注解专门用于判断是否具有角色的,能写在方法或类上。参数要以 ROLE_开头
配置类开启@EnableGlobalMethodSecurity(jsr250Enabled = true,securedEnabled = true,prePostEnabled = true)
3.支持表达式注解
配置类开启@EnableGlobalMethodSecurity(jsr250Enabled = true,securedEnabled = true,prePostEnabled = true)
使用@PreAuthorize和@PostAuthorize 在方法之前,之后进行访问控制
授权原理:FilterSecurityInterceptor -> doFilter方法 -> invoke-> beforeInvocation -> attemptAuthorization -> decide(AffirmativeBased) -> vote 投票是否通过授权通过则可以访问资源,失败则跳转到登录页面认证