您现在的位置是:首页 >其他 >spring security(密码编码器、授权,会话)网站首页其他

spring security(密码编码器、授权,会话)

sgmwgntw 2024-07-02 12:01:03
简介spring security(密码编码器、授权,会话)

目录

密码编码器

授权决策

AffirmativeBased

ConsensusBased

UnanimousBased

授权

web授权

HttpSecurity常用方法及说明

方法授权

会话控制 

会话超时 

安全会话cookie


密码编码器

Spring Security为了适应多种多样的加密类型,又做了抽象,DaoAuthenticationProvider通过 PasswordEncoder接口进行密码的对比,而具体的密码对比细节取决于实现: 

可以看到,PasswordEncoder接口中一共三个方法:

  • encod方法:该方法用来给明文密码进行加密
  • matches方法:该方法用来进行密码对比
  • upgradeEncoding方法:该方法用来判断当前密码是否需要升级,默认返回false表示不需要升级

 Spring Security提供很多内置的PasswordEncoder,能够开箱即用,使用某种PasswordEncoder只需要进行如下声明即可,如下:

@Bean
    public PasswordEncoder passwordEncoder(){
        // 定义明文比较
        return NoOpPasswordEncoder.getInstance();
    }

PasswordEncoder中常见的实现类:

  • BCryptPasswordEncoder:使用BCrypt强哈希函数加密密码。
  • NoOpPasswordEncoder:不对密码进行加密,即明文存储密码。
  • Pbkdf2PasswordEncoder:使用PBKDF2强哈希函数加密密码。
  • SCryptPasswordEncoder:使用SCrypt强哈希函数加密密码。
  • StandardPasswordEncoder:使用SHA-256哈希函数加密密码。

授权决策

AccessDecisionManager采用投票的方式来确定是否能够访问受保护资源。

 通过上图可以看出,AccessDecisionManager中包含的一系列AccessDecisionVoter将会被用来对Authentication 是否有权访问受保护对象进行投票,AccessDecisionManager根据投票结果,做出最终决策。

AccessDecisionVoter 是一个接口,其中定义有三个方法,具体结构如下所示。

 

vote() 方法的返回结果会是 AccessDecisionVoter 中定义的三个常量之一。
  • ACCESS_GRANTED表示同意
  • ACCESS_DENIED表示拒绝
  • ACCESS_ABSTAIN表示弃权
如果一个 AccessDecisionVoter 不能判定当前Authentication是否拥有访问对应受保护对象的权限,则其 vote() 方法的返回值应当为弃权 ACCESS_ABSTAIN 。Spring Security内置了三个基于投票的 AccessDecisionManager 实现类如下,它们分别是
  • AffirmativeBased
  • ConsensusBased
  • UnanimousBased

AffirmativeBased

其逻辑是:

  1. 只要有AccessDecisionVoter的投票为ACCESS_GRANTED则同意用户进行访问;
  2. 如果全部弃权也表示通过;
  3. 如果没有一个人投赞成票,但是有人投反对票,则将抛出AccessDeniedException
Spring security 默认使用的是 AffirmativeBased

ConsensusBased

其逻辑是:
  1. 如果赞成票多于反对票则表示通过。
  2. 反过来,如果反对票多于赞成票则将抛出AccessDeniedException。
  3. 如果赞成票与反对票相同且不等于0,并且属性allowIfEqualGrantedDeniedDecisions的值为true(值默认为true),则表示通过,否则将抛出异常AccessDeniedException。
  4. 如果所有的AccessDecisionVoter都弃权了,则将视参数allowIfAllAbstainDecisions的值(值默认为false)而定,如果该值 为true则表示通过,否则将抛出异常AccessDeniedException。

UnanimousBased

它的逻辑与另外两种实现有点不一样,另外两种会一次性把受保护对象的配置属性全部传递 给AccessDecisionVoter进行投票,而UnanimousBased会一次只传递一个ConfifigAttribute给 AccessDecisionVoter进行投票。这也就意味着如果我们AccessDecisionVoter的逻辑是只要传递进来的 ConfifigAttribute中有一个能够匹配则投赞成票,但是放到UnanimousBased中其投票结果就不一定是赞成了。 UnanimousBased的逻辑具体来说是这样的:

  1. 如果受保护对象配置的某一个ConfifigAttribute被任意的AccessDecisionVoter反对了,则将抛出AccessDeniedException
  2. 如果没有反对票,但是有赞成票,则表示通过。
  3. 如果全部弃权了,则将视参数allowIfAllAbstainDecisions的值而定,true则通过,false则抛出 AccessDeniedException

Spring Security也内置一些投票者实现类如RoleVoterAuthenticatedVoterWebExpressionVoter等,可以自行查阅资料进行学习。

授权

授权的方式包括 web授权和方法授权,web授权是通过 url拦截进行授权,方法授权是通过 方法拦截进行授权。他 们都会调用accessDecisionManager进行授权决策,若为web授权则拦截器为FilterSecurityInterceptor;若为方法授权则拦截器为MethodSecurityInterceptor。如果同时通过web授权和方法授权则先执行web授权,再执行方法授权,最后决策通过,则允许访问资源,否则将禁止访问。

类关系如下:  

web授权

通过给 http.authorizeRequests() 添加多个子节点来定制需求到我们的URL,如下代码:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    //安全拦截机制(最重要)
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()//屏蔽CSRF控制,即spring security不再限制CSRF
                .authorizeRequests()
                .antMatchers("/r/r1").hasAuthority("p1")//将资源与权限绑定
                .antMatchers("/r/r2").hasAuthority("p2")//将资源与权限绑定
                .antMatchers("/r/r3").hasRole("管理员")//将资源与角色绑定
                .antMatchers("/r/r4").hasAnyAuthority("p1","p2")//将资源与权限数组绑定
                .antMatchers("/r/**").authenticated()//所有/r/**的请求必须认证通过
                .anyRequest().permitAll()//除了/r/**,其它的请求可以访问
                .and()
                .formLogin()//允许表单登录
                .loginPage("/login-view")//指定我们自己的登录页,spring security以重定向方式跳转到/login-view
                .loginProcessingUrl("/login")//指定登录处理的URL,也就是用户名、密码表单提交的目的路径
                .successForwardUrl("/login-success");//指定登录成功后的跳转URL

        // 自定义会话控制
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);

        // 自定义退出
        http.logout()
                .logoutUrl("/logout")// 退出的url
                .logoutSuccessUrl("/login-view");// 退到/login-view
    }
}

注:规则的顺序是重要的,更具体的规则应该先写

HttpSecurity常用方法及说明

方法说明
authenticated()保护URL,需要用户登录
permitAll()指定URL无需保护,一般应用与静态资源文件
hasRole(String role)限制单个角色访问,角色将被增加 “ROLE_” .所以”ADMIN” 将和 “ROLE_ADMIN”进行比较.
hasAuthority(String authority)限制单个权限访问
hasAnyRole(String… roles)允许多个角色访问
hasAnyAuthority(String… authorities) 允许多个权限访问
access(String attribute)该方法使用 SpEL表达式, 所以可以创建复杂的限制
hasIpAddress(String ipaddressExpression)限制IP地址或子网
openidLogin()用于基于 OpenId 的验证
headers()将安全标头添加到响应
cors()配置跨域资源共享( CORS )
sessionManagement()允许配置会话管理
portMapper()    允许配置一个PortMapper(HttpSecurity#(getSharedObject(class))),其他提供SecurityConfigurer的对象使用 PortMapper 从 HTTP 重定向到 HTTPS 或者从 HTTPS 重定向到 HTTP。默认情况下,Spring Security使用一个PortMapperImpl映射 HTTP 端口8080到 HTTPS 端口8443,HTTP 端口80到 HTTPS 端口443
jee()配置基于容器的预认证。 在这种情况下,认证由Servlet容器管理
x509()配置基于x509的认证
rememberMe允许配置“记住我”的验证
authorizeRequests()允许基于使用HttpServletRequest限制访问
requestCache()允许配置请求缓存
exceptionHandling()允许配置错误处理
securityContext()在HttpServletRequests之间的SecurityContextHolder上设置SecurityContext的管理。 当使用WebSecurityConfigurerAdapter时,这将自动应用
servletApi()将HttpServletRequest方法与在其上找到的值集成到SecurityContext中。 当使用WebSecurityConfigurerAdapter时,这将自动应用
csrf()添加 CSRF 支持,使用WebSecurityConfigurerAdapter时,默认启用
logout()添加退出登录支持。当使用WebSecurityConfigurerAdapter时,这将自动应用。默认情况是,访问URL”/ logout”,使HTTP Session无效来清除用户,清除已配置的任何#rememberMe()身份验证,清除SecurityContextHolder,然后重定向到”/login?success”
anonymous()允许配置匿名用户的表示方法。 当与WebSecurityConfigurerAdapter结合使用时,这将自动应用。 默认情况下,匿名用户将使用org.springframework.security.authentication.AnonymousAuthenticationToken表示,并包含角色 “ROLE_ANONYMOUS”
formLogin()指定支持基于表单的身份验证。如果未指定FormLoginConfigurer#loginPage(String),则将生成默认登录页面
oauth2Login()根据外部OAuth 2.0或OpenID Connect 1.0提供程序配置身份验证
requiresChannel()配置通道安全。为了使该配置有用,必须提供至少一个到所需信道的映射
httpBasic()配置 Http Basic 验证
addFilterAt()在指定的Filter类的位置添加过滤器

方法授权

 Spring Security2.0版 本开始,它支持服务层方法的安全性的支持。主要有@PreAuthorize,@PostAuthorize, @Secured三类注解。

首先要开启Spring Security的 Secured、pre、Post 注解,在配置类上添加此注解

@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)

然后在controller中根据需求使用对资源授权,实例如下:

@RestController
public class LoginController {

    @RequestMapping(value = "/login-success",produces = {"text/plain;charset=UTF-8"})
    public String loginSuccess(){
        //提示具体用户名称登录成功
        return " 登录成功";
    }

    /**
     * 测试资源1
     * @return
     */
    @GetMapping(value = "/r/r1",produces = {"text/plain;charset=UTF-8"})
    @PreAuthorize("hasAuthority('p1')")//拥有p1权限才可以访问
    public String r1(){
        return " 访问资源1";
    }

    /**
     * 测试资源2
     * @return
     */
    @GetMapping(value = "/r/r2",produces = {"text/plain;charset=UTF-8"})
    @PreAuthorize("hasAuthority('p2')")//拥有p2权限才可以访问
    public String r2(){
        return " 访问资源2";
    }
}

其@PreAuthorize中的参数与HttpSecurity的IPA相差无几

会话控制 

我们可以通过以下选项准确控制会话何时创建以及Spring Security如何与之交互:

机制描述
always如果没有session存在就创建一个
ifRequired如果需要就创建一个Session(默认)登录时
never
SpringSecurity 将不会创建 Session ,但是如果应用中其他地方创建了 Session ,那么 Spring
Security 将会使用它。
statelessSpringSecurity将绝对不会创建Session,也不使用Session

 通过在继承WebSecurityConfigurerAdapter类来进行如下配置:

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
    }
}
  • 若选用never,则指示Spring Security对登录成功的用户不创建Session了,但若你的应用程序在某地方新建了 session,那么Spring Security会用它的。
  • 若使用stateless,则说明Spring Security对登录成功的用户不会创建Session了,你的应用程序也不会允许新建 session。并且它会暗示不使用cookie,所以每个请求都需要重新进行身份验证。这种无状态架构适用于REST API及其无状态认证机制。

会话超时 

可以再sevlet容器中设置Session的超时时间,如下设置Session有效期为3600s 

spring boot 配置文件:

server.servlet.session.timeout=3600s

 session超时之后,可以通过Spring Security 设置跳转的路径。

http.sessionManagement()
.expiredUrl("/login‐view?error=EXPIRED_SESSION")
.invalidSessionUrl("/login‐view?error=INVALID_SESSION");

安全会话cookie

我们可以使用 httpOnly secure 标签来保护我们的会话 cookie
  • httpOnly:如果为true,那么浏览器脚本将无法访问cookie
  • secure:如果为true,则cookie将仅通过HTTPS连接发送
spring boot 配置文件:
server.servlet.session.cookie.http‐only=true
server.servlet.session.cookie.secure=true

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。