详解Spring Security(1)

本文涉及的产品
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介: 1.概述Spring Security是一个基于Spring框架的安全性框架,它提供了一系列的API和扩展点,可以帮助开发人员在应用程序中轻松地实现安全认证和授权控制。我们可以理解为Spring Security维护了一组我们可以自定义的访问规则,每次访问都会去进行规则比对,满足规则的才放行。这些规则可以有很多维度,本文会以最基础的基于角色的控制入手逐步扩展详细介绍Spring Security。基于角色的控制,我们可以理解为Spring Security为我们维护了一张“白名单”,而登录就是去白名单里进行比对。

1.概述

Spring Security是一个基于Spring框架的安全性框架,它提供了一系列的API和扩展点,可以帮助开发人员在应用程序中轻松地实现安全认证和授权控制。


我们可以理解为Spring Security维护了一组我们可以自定义的访问规则,每次访问都会去进行规则比对,满足规则的才放行。


这些规则可以有很多维度,本文会以最基础的基于角色的控制入手逐步扩展详细介绍Spring Security。


基于角色的控制,我们可以理解为Spring Security为我们维护了一张“白名单”,而登录就是去白名单里进行比对。


2.登录

2.1.默认用户

依赖:

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-security</artifactId>
</dependency>

依赖引入后其实spring security就已经生效了,此时访问我们的接口会自动转跳spring security内置的登录页,验证通过后才会转跳到后端接口:

473af71432104517bfcd4336f01f4ae1.png

默认用户名:user

默认密码:会在日志中输出

image.png

2.2.自定义用户

spring security给我们提供了接口来配置security,其中就包括自定义用户和角色:

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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;
@Configuration
@EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests().
                //所有用户都可以访问/all
                        antMatchers("/all").permitAll().
                //admin可以访问/admin
                        antMatchers("/admin").hasRole("admin");
        //如果验证未通过,转跳spring security自带的登录页面进行登录
        //如果不配置此处的步骤,验证未通过则会直接返回403访问被拒绝。
        http.formLogin();
    }
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //自定义两个用户user、admin,user对应user角色,admin对应admin角色
        auth.inMemoryAuthentication()
                .withUser("user").password("123").roles("user")
                .and()
                .withUser("admin").password("456").roles("user", "admin");
    }
}

2.3.加密

完成上面的配置这时候我们再访问我们的接口,转跳登录页后输入我们定义的有对应访问用户名密码,比如admin 456,似乎就应该能正常访问到我们的接口了。但是实际上会报错:


java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"


这是因为我们在定义用户的时候密码没有加密,自spring security 5.0开始就要求必须对密码进行加密,否则,在进行密码比较时,就会出现无法解析密码加密算法的异常。我们当前用的主流版本一定是5.0以上,所以加密是必须的。


spring security提供了一个PasswordEncoder接口给我们自定器,在加密器里可以通过方法重写来自定义加密过程。但是实际使用上不用费力去自己写一个加密算法,security给我们准备了多种加密器、多种加密方法,而且自己写的也肯定没有开源的稳定和好用。


spring security提供的加密器如下:


BCryptPasswordEncoder:这是最常用的加密算法之一,它使用哈希和随机盐来加密密码。


Pbkdf2PasswordEncoder:这也是一种密码加密算法,它使用基于密码的密钥导出函数(PBKDF2)来加密密码。


SCryptPasswordEncoder:这是一种基于内存的密码哈希算法,它使用大量的内存来防止散列碰撞攻击。


NoOpPasswordEncoder:这是一种不安全的加密算法,它仅仅是将明文密码作为加密后的密码。不建议在生产环境中使用。


我们就挑选BCryptPasswordEncoder来对密码进行加密,修改一下,在定义用户名密码的时候BCryptPasswordEncoder来进行加密:

 @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //自定义两个用户user、admin,user对应user角色,admin对应admin角色
        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                .withUser("user").password("123").roles("user")
                .and()
                .withUser("admin").password("456").roles("user", "admin");
    }

加密之后我们再在登陆页面进行登录的时候就不能用明文的admin 456来进行登录了,需要将456使用同样的加密算法加密后的密文密码来登录。


2.4.绕过加密

在实际生产环境种使用密文密码当然是无可厚非的,但是在开发、测试阶段会很麻烦,有没有绕过的办法喃?有的,{noop}","no operation"的缩写,表示不执行任何操作。如果密码的前缀是"{noop}",则Spring Security会将其识别为明文密码,不进行加密,直接存储到数据库或内存中。可以将上面的代码改成:

@Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //自定义两个用户user、admin,user对应user角色,admin对应admin角色
        auth.inMemoryAuthentication()
                .withUser("user").password("{noop}123").roles("user")
                .and()
                .withUser("admin").password("{noop}456").roles("user", "admin");
    }

2.5.怎么传递用户信息

由于我们的请求我们都是在登陆界面输入用户名密码,实际使用中不可能每次都这样去做,怎么把用户名、密码携带在请求里传给security喃?这却决于配置为哪种,支持两种方式:

  • 表单
  • 放在请求头中

表单:

后端配置:

@Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/", "/home").permitAll()
                .anyRequest().authenticated()
                .and()
                //指定用表单的方式登录
                .formLogin()
                //登录地址,不配置的话会有默认值,默认是login,可以用这个配置来设置新的请求页
                .loginPage("/login")
                //配置用户名的参数名,不配置的话会有默认值,默认是username
                .usernameParameter("username")
                //配置密码的参数名,不配置的话会有默认值,默认是password
                .passwordParameter("password")
                .permitAll()
    }

前端表单:

<form action="/login" method="post">
  <label for="username">Username:</label>
  <input type="text" id="username" name="username"><br><br>
  <label for="password">Password:</label>
  <input type="password" id="password" name="password"><br><br>
  <input type="submit" value="Submit">
</form>

当用户提交表单后,会向后端发送一个POST请求,请求的URL为/login,请求参数包含用户名和密码。

放在表头中:

在请求头中添加Authorization字段,将用户名和密码进行Base64编码后传递给后端进行验证。后端配置示例:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .authorizeRequests()
        .antMatchers("/", "/home").permitAll()
        .anyRequest().authenticated()
        .and()
        .httpBasic()
        .permitAll();
}

2.6.记住我

security支持“记住我”这个功能,是基于token来实现得,开启记住我后,登陆成功后会返给客户端一个cookie,用这个cookie来实现记住我的效果,配置示例如下:

@Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests().
                antMatchers("/all").permitAll().
                antMatchers("/admin").hasRole("admin").and()
                .formLogin().and()
                //开启记住我功能
                .rememberMe()
                //设置返回的cookie的键名
                .rememberMeParameter("remember-me")
                //设置过期时间
                .tokenValiditySeconds(7*24*60*60);
    }

目录
相关文章
|
25天前
|
JSON 安全 Java
什么是JWT?如何使用Spring Boot Security实现它?
什么是JWT?如何使用Spring Boot Security实现它?
103 5
|
5月前
|
安全 Java 数据安全/隐私保护
使用Spring Security实现细粒度的权限控制
使用Spring Security实现细粒度的权限控制
|
5月前
|
安全 Java 数据库
实现基于Spring Security的权限管理系统
实现基于Spring Security的权限管理系统
|
5月前
|
安全 Java 数据安全/隐私保护
解析Spring Security中的权限控制策略
解析Spring Security中的权限控制策略
|
6月前
|
JSON 安全 Java
Spring Security 6.x 微信公众平台OAuth2授权实战
上一篇介绍了OAuth2协议的基本原理,以及Spring Security框架中自带的OAuth2客户端GitHub的实现细节,本篇以微信公众号网页授权登录为目的,介绍如何在原框架基础上定制开发OAuth2客户端。
238 4
Spring Security 6.x 微信公众平台OAuth2授权实战
|
6月前
|
存储 安全 Java
Spring Security 6.x OAuth2登录认证源码分析
上一篇介绍了Spring Security框架中身份认证的架构设计,本篇就OAuth2客户端登录认证的实现源码做一些分析。
277 2
Spring Security 6.x OAuth2登录认证源码分析
|
6月前
|
安全 Java 数据安全/隐私保护
Spring Security 6.x 一文快速搞懂配置原理
本文主要对整个Spring Security配置过程做一定的剖析,希望可以对学习Spring Sercurity框架的同学所有帮助。
326 5
Spring Security 6.x 一文快速搞懂配置原理
|
6月前
|
安全 Java API
Spring Security 6.x 图解身份认证的架构设计
【6月更文挑战第1天】本文主要介绍了Spring Security在身份认证方面的架构设计,以及主要业务流程,及核心代码的实现
99 1
Spring Security 6.x 图解身份认证的架构设计
|
5月前
|
安全 Java 数据安全/隐私保护
使用Spring Security实现细粒度的权限控制
使用Spring Security实现细粒度的权限控制
|
5月前
|
安全 Java 数据安全/隐私保护
使用Java和Spring Security实现身份验证与授权
使用Java和Spring Security实现身份验证与授权