springboot3整合SpringSecurity实现登录校验与权限认证(万字超详细讲解)

目前市面上常用的安全框架有:

Spring Security、Shiro,还有一个国人开发的框架目前也备受好评:SaToken

但是与spring boot项目融合度最高的还是Spring Security,所以目前我们讲解一下基于spring boot项目来整合spring security来实现常用的登录校验与权限认证;

Spring Security(安全框架)
1、介绍
Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。

如果项目中需要进行权限管理,具有多个角色和多种权限,我们可以使用Spring Security。

采用的是责任链的设计模式,是一堆过滤器链的组合,它有一条很长的过滤器链。

2、功能
Authentication (认证),就是用户登录
Authorization (授权),判断用户拥有什么权限,可以访问什么资源
安全防护,跨站脚本攻击,session攻击等
非常容易结合Springboot项目进行使用,本次就着重与实现认证和授权这两个功能
 


版本spring boot3.1.16、spring security6.x

身份认证:

1、创建一个spring boot项目,并导入一些初始依赖:


    
        org.springframework.boot
        spring-boot-starter-security
    
    
        org.springframework.boot
        spring-boot-starter-web
    
    
        com.alibaba
        fastjson
        2.0.21
    

    
        com.mysql
        mysql-connector-j
        runtime
    
    
        org.projectlombok
        lombok
        true
    
    
        org.springframework.boot
        spring-boot-starter-test
        test
    
    
        org.springframework.security
        spring-security-test
        test
    

2、由于我们加入了spring-boot-starter-security的依赖,所以security就会自动生效了。这时直接编写一个controller控制器,并编写一个接口进行测试:

springboot3整合SpringSecurity实现登录校验与权限认证(万字超详细讲解)_第1张图片

可以看到我们在访问这个接口时出现了拦截,必须要我们进行登录之后才能访问;

springboot3整合SpringSecurity实现登录校验与权限认证(万字超详细讲解)_第2张图片

那么接下来我们就来实现第一个功能:用户登录认证;

3、自定义用户的登录认证:

Spring Security 6.x 的认证实现流程如下:

  1. 用户提交登录请求
  2. Spring Security 将请求交给 UsernamePasswordAuthenticationFilter 过滤器处理。
  3. UsernamePasswordAuthenticationFilter 获取请求中的用户名和密码,并生成一个 AuthenticationToken 对象,将其交给 AuthenticationManager 进行认证。
  4. AuthenticationManager 通过 UserDetailsService 获取用户信息,然后使用 PasswordEncoder 对用户密码进行校验。
  5. 如果密码正确,AuthenticationManager 会生成一个认证通过的 Authentication 对象,并返回给 UsernamePasswordAuthenticationFilter 过滤器。如果密码不正确,则 AuthenticationManager 抛出一个 AuthenticationException 异常。
  6. UsernamePasswordAuthenticationFilter 将 Authentication 对象交给 SecurityContextHolder 进行管理,并调用 AuthenticationSuccessHandler 处理认证成功的情况。
  7. 如果认证失败,UsernamePasswordAuthenticationFilter 会调用 AuthenticationFailureHandler 处理认证失败的情况。

看起来有点复杂,其实写起来很简单的。spring security的底层就是一堆的过滤器来是实现的,而我们只需要编写一些重要的过滤器即可,其他的就用spring security默认的实现,只要不影响我们正常的登录功能即可。

(创建一个用户表用来进行登录实现,注意这个表中的用户名不能重复,我们将用户名作为每一个用户的唯一凭证,就如同人身份证号一样)表的结构非常简单,一些配置我这里就不在描述了(实体类、mapper、service、controller等)

springboot3整合SpringSecurity实现登录校验与权限认证(万字超详细讲解)_第3张图片

认证的实现流程:

1、创建一个UserDetailsService实现SpringSecurity的UserDetailsService接口(这里写的是查询用户的逻辑)

UserDetailsService:此接口中定义了登录服务方法,用来实现登录逻辑。方法的返回值是UserDetails,也是spring security框架定义中的一个接口,用来存储用户信息,我们可以自定义一个类用来实现这个接口,将来返回的时候就返回我们自定义的用户实体类。

实现UserDetailsService接口

@Component
public class MyUserDetailsService implements UserDetailsService {

    /*
    *  UserDetailsService:提供查询用户功能,如根据用户名查询用户,并返回UserDetails
     *UserDetails,SpringSecurity定义的类, 记录用户信息,如用户名、密码、权限等
    * */
    @Autowired
    private SysUserMapper sysUserMapper;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//根据用户名从数据库中查询用户
        SysUser sysUser = sysUserMapper.selectOne(new LambdaQueryWrapper()
                .eq(username != null, SysUser::getUsername, username));
if (sysUser==null){
    throw new UsernameNotFoundException("用户不存在");
}
        MySysUserDetails mySysUserDetails=new MySysUserDetails(sysUser);
        return mySysUserDetails;
    }
}

(在原有数据库表的基础上)实现UserDetails接口:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class MySysUserDetails implements UserDetails {


    private Integer id;

    private String username;

    private String password;

//    用户拥有的权限集合,我这里先设置为null,将来会再更改的
    @Override
    public Collection getAuthorities() {
        return null;
    }

    public MySysUserDetails(SysUser sysUser) {
        this.id = sysUser.getId();
        this.username = sysUser.getUsername();
        this.password = sysUser.getPassword();
    }

    //    后面四个方法都是用户是否可用、是否过期之类的。我都设置为true
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

2、通过配置类对AuthenticationManager与自定义的UserDetails和PasswordEncoder进行关联

Spring Security是通过AuthenticationManager实现的认证,会借此来判断用户名和密码的正确性

密码解析器spring security框架定义的接口:PasswordEncoder

spring security框架强制要求,必须在spring容器中存在PasswordEncoder类型对象,且对象唯一

@Configuration
@EnableWebSecurity //开启webSecurity服务
public class SecurityConfig {

    
    @Autowired
    private MyUserDetailsService myUserDetailsService;

@Bean
public AuthenticationManager authenticationManager(PasswordEncoder passwordEncoder){
    DaoAuthenticationProvider provider=new DaoAuthenticationProvider();
//将编写的UserDetailsService注入进来
    provider.setUserDetailsService(myUserDetailsService);
//将使用的密码编译器加入进来
    provider.setPasswordEncoder(passwordEncoder);
//将provider放置到AuthenticationManager 中
    ProviderManager providerManager=new ProviderManager(provider);
    return providerManager;
}
    /*
     * 在security安全框架中,提供了若干密码解析器实现类型。
     * 其中BCryptPasswordEncoder 叫强散列加密。可以保证相同的明文,多次加密后,
     * 密码有相同的散列数据,而不是相同的结果。
     * 匹配时,是基于相同的散列数据做的匹配。
     * Spring Security 推荐使用 BCryptPasswordEncoder 作为密码加密和解析器。
     * */
@Bean
    public PasswordEncoder passwordEncoder(){
    return new BCryptPasswordEncoder();
}

3、在登录方法所在的类中注入AuthenticationManager,调用authenticate实现认证逻辑,并且在认证之后返回认证过的用户信息:

controller层:

//    用户登录
    @PostMapping("/login")
    public String login(@RequestBody LoginDto loginDto){

      String token=  sysUserService.login(loginDto);
        return token;
    }

对应的service层的方法:

  @Autowired
    private AuthenticationManager authenticationManager;

//    登录接口的具体实现
    @Override
    public String login(LoginDto loginDto) {
//        传入用户名和密码
        UsernamePasswordAuthenticationToken usernamePassword =
                new UsernamePasswordAuthenticationToken(loginDto.getUsername(),loginDto.getPassword());
//是实现登录逻辑,此时就回去调用LoadUserByUsername方法
        Authentication authenticate = authenticationManager.authenticate(usernamePassword);
//        获取返回的用户信息
        Object principal = authenticate.getPrincipal();
     //强转为MySysUserDetails类型
MySysUserDetails mySysUserDetails = (MySysUserDetails) principal;
//        输出用户信息
        System.err.println(mySysUserDetails);
//返回token
        String token= UUID.randomUUID().toString();
        return token;
    }

  我在test类中设置一些用户数据,并进行测试;

@Autowired private SysUserMapper sysUserMapper;

@Autowired private PasswordEncoder passwordEncoder;

@Test void contextLoads() { //导入了一个用户

SysUser sysUser=new SysUser();

sysUser.setUsername("zhangsan"); sysUser.setPassword(passwordEncoder.encode("123456")); sysUserMapper.insert(sysUser);

}

这里我们已经写好了自定义的登录流程,将项目运行起来(我同时还写了一个普通的test方法,类型是get,用来一起测试)

访问http://localhost:8080/test

这是我们写的一个普通的get方法,我们明明访问的是http://localhost:8080/test这个路径,但是却自动跳转到了Spring Security提供的默认的登录页面;这是因为Spring Security默认所有的请求都要先登录才行,我们在这里登录之后就可以继续访问test页面了;

(由于我们已经实现了UserDetailsService接口,并且在用户表中导入了一条用户数据,那么,这里的用户名和密码就是我们在数据库中存储的用户名和密码)

springboot3整合SpringSecurity实现登录校验与权限认证(万字超详细讲解)_第4张图片

登录成功之后,我们就可以访问到test的信息了:

springboot3整合SpringSecurity实现登录校验与权限认证(万字超详细讲解)_第5张图片

既然这个test请求要先进行拦截认证才能访问,那么,我们刚才编写的登录接口sys-user/login岂不是也要先进行拦截认证才能访问,这就与我们编写登录接口的初衷违背了,我们这个接口就是用来登陆的,现在还要先登录认证,之后再访问这个登录接口。那么有没有一种方法,不使用SpringSecurity默认的登录页面呢,使我们编写的登录接口所有人都可以直接访问呢?

4、使用(SecurityFilterChain)过滤器, 配置用户登录的接口可以暴露出来,被所有人都正常的访问(还应在暴露一个注册接口,但我这里就先不写了)

还是在第二步设置的SecurityConfig类中设置过滤器:

在spring security6.x版本之后,原先经常用的and()方法被废除了,现在spring官方推荐使用Lambda表达式的写法。

(因为我们接下来要进行测试,所以禁用CSRF保护,CSRF(Cross-Site Request Forgery)是一种攻击方式,攻击者通过伪造用户的请求来执行恶意操作。)

 /*
     * 配置权限相关的配置
     * 安全框架本质上是一堆的过滤器,称之为过滤器链,每一个过滤器链的功能都不同
     * 设置一些链接不要拦截
     * */
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
//       关闭csrf
        httpSecurity.csrf(it->it.disable());
httpSecurity.authorizeHttpRequests(it->
        it.requestMatchers("/sys-user/login").permitAll()  //设置登录路径所有人都可以访问
                .anyRequest().authenticated()  //其他路径都要进行拦截
        );

        return httpSecurity.build();
    }

5、将项目运行起来(我同时还写了一个普通的test方法,类型是get,没有放行,用于测试能不能拦截到):

访问test请求:遇到拦截,说明我们的配置生效了

springboot3整合SpringSecurity实现登录校验与权限认证(万字超详细讲解)_第6张图片

访问登录页面:能正常访问,且密码正确,返回了一个我们自己生成的一个token。

springboot3整合SpringSecurity实现登录校验与权限认证(万字超详细讲解)_第7张图片

6、自定义一个登录页面:

SpringSecurity虽然默认有一个登录页面,但是我们一般情况下还是用我们自己写的登录页面,这样可操作性就大了很多;

引入thymeleaf依赖,我们直接在idea项目中建立一个登录页面;

编写一个登录页面,主要是完成用户的登录,同时我们也不再需要频繁的使用postman进行测试了:




    
    自定义的登录页面



用户名:
密码:

这是一个简单的登录页面,就指定了用户名和密码。

并且指定from表单的提交路径为我们自定义的登录接口;将这个页面放在resource/templates目录下,方便我们将来的调用;

HTML中的form表单默认情况下会将数据格式化为key-value形式,而不是JSON格式。

也就是说我们刚刚写的自定义登录接口时是用@RequestBody接受收json类型的数据,这肯定是接受不到的,有两种方法实现:

1、直接用@RequestParam("username")  ,@RequestParam("password")接收这两个参数

2、@ModelAttribute注解:@ModelAttribute("formData") User user   //在@ModelAttribute注解内写表单的id,还能使用对象进行接收

我们也可以在前端将from表单的数据转化为json之后,在进行发送,但那样需要写js,我就直接在后端改一下了。

还是使用使用(SecurityFilterChain)过滤器,指定我们自定义的登录表单路径:

 /*
     * 配置权限相关的配置
     * 安全框架本质上是一堆的过滤器,称之为过滤器链,每一个过滤器链的功能都不同
     * 设置一些链接不要拦截
     * */

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
//       关闭csrf
        httpSecurity.csrf(it->it.disable());
//        配置路径相关
httpSecurity.authorizeHttpRequests(it->
        it.requestMatchers("/login","sys-user/login").permitAll()  //设置登录路径所有人都可以访问
                .anyRequest().authenticated()  //其他路径都要进行拦截
        );
//表单
httpSecurity.formLogin(from->
        from.loginPage("/login")   //跳转到自定义的登录页面
.loginProcessingUrl("/sys-user/login")  //处理前端的请求,与from表单的action一致即可
     .defaultSuccessUrl("/index")  //默认的请求成功之后的跳转页面,直接访问登录页面
        );

        return httpSecurity.build();
    }

注意,这里还需要将/login这个接口进行放行。

我们知道,不能直接访问login.html这个自定义的登录页面,但是我们可以使用路径映射。先写一个login的get请求,并将这个请求映射到login.html页面。

.defaultSuccessUrl("/index"):这个方法是我们默认的登录成功之后跳转的请求地址。

如果你之前有请求的地址,但是这个地址没有放行或者你没有登录,那么会自动跳转到我们自定义的登录页面,完成登录之后,会跳转到你最先访问的地址;如果你直接访问的就是/login登录地址,那么默认的登录成功之后跳转到我们指定的地址:/index

@Controller
public class Login {


@GetMapping("/login")
public String login(){
    System.out.println("用户进入登录页面");
    return "login";   //没使用json返回,直接映射到自定义登录的页面
}

@GetMapping("/index")
@ResponseBody
    public String index(){
    return "用户登录成功";
}
}

现在我们已经自定义了一个登录页面,将项目启动起来进行测试:

我访问/test地址,这个地址没有放行,而且我们这是没有登录,那么会自动跳转到我们自定义的登录页面:

springboot3整合SpringSecurity实现登录校验与权限认证(万字超详细讲解)_第8张图片我们进行登录之后,会跳转到/test请求地址:

springboot3整合SpringSecurity实现登录校验与权限认证(万字超详细讲解)_第9张图片

可以看到我们的结果与我们设想的一样:

现在我们直接访问/login登录页面:可以看到返回了/index页面的内容(这个是我们设置的默认登录成功之后返回的页面)

springboot3整合SpringSecurity实现登录校验与权限认证(万字超详细讲解)_第10张图片

权限校验:

我们费了很多功夫完成了身份认证,权限校验相对来说是比较简单的:

首先,我先解释一下角色与权限在SpringSecurity中的作用:

  1. 角色(Role):角色是一组权限的集合,通常代表着用户的身份或职责。在Spring Security中,可以通过配置将角色分配给用户或者用户组,以此来控制用户对系统资源的访问。例如,管理员拥有添加、删除和修改用户的权限,而普通用户只能查看自己的信息。

  2. 权限(Permission):权限是指对某一特定资源的访问控制,例如读写文件、访问数据库等。在Spring Security中,通常使用“资源-操作”命名方式来定义权限,例如“/admin/* - GET”表示允许访问以/admin/开头的所有URL的GET请求。可以将权限分配给角色,也可以将其分配给单独的用户。

角色与权限之间的关系是多对多的;

建立两张简单的表;一张用来存放角色、一张用来存放权限

角色表:

springboot3整合SpringSecurity实现登录校验与权限认证(万字超详细讲解)_第11张图片

权限表:

springboot3整合SpringSecurity实现登录校验与权限认证(万字超详细讲解)_第12张图片

这里建立的两张表只是用来进行测试,正常的数据不可能这么少的。建立相应的实体类;

SpringSecurity要求将身份认证信息存到GrantedAuthority对象列表中。代表了当前用户的权限。 GrantedAuthority对象由AuthenticationManager插入到Authentication对象中,然后在做出授权决策 时由AccessDecisionManager实例读取。 GrantedAuthority 接口只有一个方法

AuthorizationManager实例通过该方法来获得GrantedAuthority。通过字符串的形式表示, GrantedAuthority可以很容易地被大多数AuthorizationManager实现读取。如果GrantedAuthority不 能精确地表示为String,则GrantedAuthorization被认为是复杂的,getAuthority()必须返回null

告知权限的流程:

直接在登录时查询用户的权限,并放在我们自定义的实现了UserDetail的接口类中,用来表示登录用户的全部信息;

在MySysUserDetails类中加入两个属性,记录从数据库中查处的角色和权限信息

springboot3整合SpringSecurity实现登录校验与权限认证(万字超详细讲解)_第13张图片

我这里就简单一点,不在做多表关联查询了。直接把zhangsan用户设置为超级管理员,拥有所有权限;lisi用户设置为普通管理员,拥有基本权限。

在MyUserDetailsService中实现用户权限的赋值:

@Component
public class MyUserDetailsService implements UserDetailsService {

    /*
    *  UserDetailsService:提供查询用户功能,如根据用户名查询用户,并返回UserDetails
     *UserDetails,SpringSecurity定义的类, 记录用户信息,如用户名、密码、权限等
    * */
    @Autowired
    private SysUserMapper sysUserMapper;

@Autowired
private SysRoleMapper sysRoleMapper;
@Autowired
private SysPermissionsMapper sysPermissionsMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//根据用户名从数据库中查询用户
        SysUser sysUser = sysUserMapper.selectOne(new LambdaQueryWrapper()
                .eq(username != null, SysUser::getUsername, username));
if (sysUser==null){
    throw new UsernameNotFoundException("用户不存在");
}
        MySysUserDetails mySysUserDetails=new MySysUserDetails(sysUser);
if ("zhangsan".equals(username)){
//zhangsan用户是超级管理员,拥有一切权限
    SysRole sysRole = sysRoleMapper.selectOne(new LambdaQueryWrapper().eq(SysRole::getRoleName, "超级管理员"));
    Set roles=new HashSet<>();
    roles.add(sysRole);
    mySysUserDetails.setRoles(roles);
    SysPermissions sysPermissions = sysPermissionsMapper.selectById(1);
    Set permissions=new HashSet<>();
    permissions.add(sysPermissions.getPermissionsName());
    mySysUserDetails.setPermissions(permissions);
}

        if ("lisi".equals(username)){
//lisi用户是普通管理员,拥有基本权限
            SysRole sysRole = sysRoleMapper.selectOne(new LambdaQueryWrapper().eq(SysRole::getRoleName, "普通管理员"));
            Set roles=new HashSet<>();
            roles.add(sysRole);
            mySysUserDetails.setRoles(roles);
            SysPermissions sysPermissions = sysPermissionsMapper.selectById(2);
            Set permissions=new HashSet<>();
            permissions.add(sysPermissions.getPermissionsName());
            mySysUserDetails.setPermissions(permissions);
        }

        return mySysUserDetails;
    }
}

在实现了UserDetailes接口的用户信息类MySysUserDetails中完成角色和权限的赋值:
 

//    角色信息
    private Set roles;
//    权限信息
    private Set permissions;
    
//    用户拥有的权限集合,我这里先设置为null,将来会再更改的
    @Override
    public Collection getAuthorities() {
        System.err.println("进入权限的获取方法");

        List authorities = new ArrayList<>(); // 授权信息列表
// 将角色名称添加到授权信息列表中
        roles.forEach(role->
        authorities.add(new SimpleGrantedAuthority(role.getRoleName())));
        // 将权限名称添加到授权信息列表中
        permissions.forEach(permission->
        authorities.add(new SimpleGrantedAuthority(permission))
                );
        return authorities; // 返回授权信息列表
    }

用户认证之后,会去存储用户对应的权限,并且给资源设置对应的权限,SpringSecurity支持两种粒度 的权限:

1、基于请求的:在配置文件中配置路径,可以使用**的通配符

2、基于方法的:在方法上使用注解实现

角色配置:在UserDetails接口中存在相关的权限和角色管理,只不过我们在实现这个接口的时候,将这些都设置为了null。现在我们只需要将这些信息实现即可:

1、基于请求:

还是在SecurityFilter过滤器中实现请求地址的权限校验

httpSecurity.authorizeHttpRequests(it->
//hello地址只有超级管理员角色才能访问
it.requestMatchers("/hello").hasRole("超级管理员")
//hello2地址只有"拥有所有权限"的权限才能访问
.requestMatchers("hello2").hasAuthority("拥有所有权限")
                .requestMatchers("/login","sys-user/login").permitAll()  //设置登录路径所有人都可以访问
                .anyRequest().authenticated()  //其他路径都要进行拦截
        );

使用sili进行登录时,访问hello2接口显示权限不够:

springboot3整合SpringSecurity实现登录校验与权限认证(万字超详细讲解)_第14张图片

使用zhangsan进行登录时,访问hello2接口可以访问到:

springboot3整合SpringSecurity实现登录校验与权限认证(万字超详细讲解)_第15张图片

2、基于方法:

基于方法的权限认证要在SecurityConfig类上加上@EnableMethodSecurity注解,表示开启了方法权限的使用;

常用的有四个注解:

@PreAuthorize

@PostAuthorize

@PreFilter

@PostFilter

/*测试@PreAuthorize注解
* 作用:使用在类或方法上,拥有指定的权限才能访问(在方法运行前进行校验)
* String类型的参数:语法是Spring的EL表达式
* 有权限:test3权限
* hasRole:会去匹配authorities,但是会在hasRole的参数前加上一个ROLE_前缀,
* 所以在定义权限的时候需要加上ROLE_前缀
* role和authorities的关系是:role是一种复杂的写法,有ROLE_前缀,authorities是role的简化写法
* 如果使用
* hasAnyRole:则匹配的权限是在authorities加上前缀ROLE_
* 推荐使用
* hasAnyAuthority:匹配authorities,但是不用在authorities的参数前加上ROLE_前缀
* */
@PreAuthorize("hasAnyAuthority('拥有所有权限')")
@ResponseBody
@GetMapping("/test3")
public String test3(){
    System.out.println("一个请求");

    return "一个test3请求";
}
/*
 @PostAuthorize:在方法返回时进行校验。
 可以还是校验权限、或者校验一些其他的东西(接下来我们校验返回值的长度)
*返回结果的长度大于3、则认为是合法的
returnObject:固定写法,代指返回对象
* */
@ResponseBody
@PostAuthorize("returnObject.length()>4")
@GetMapping("/test4")
public String test4(){
    System.out.println("一个test4请求");

    return "小张自傲张最终";
}
    /*
* @PreFilter:过滤符合条件的数据进入到接口
* */
    @PostFilter("filterObject.length()>3")
    @ResponseBody
    @GetMapping("/test5")
    public String test5(){
        System.out.println("一个test4请求");
List list = new ArrayList<>();
list.add("张三");
list.add("王麻子");
list.add("狗叫什么");

        return "一个test5请求";
    }
/*
* @PreFilter:过滤符合条件的数据返回,数据必须是Collection、map、Array【数组】
* */
@PreFilter("filterObject.length()>5")
@ResponseBody
@PostMapping("/test6")
public List test6(@RequestBody List list){
    return list;
}

这四个常用的权限校验方法我都写出来了,运行结果我就不在一一截图了。

需要注意的是这些方法不仅仅局限在权限的校验,还能对返回的结果做一定的操作;

最需要注意的就是@PreFilter注解,它要求前端传递的参数一定是数组或集合;

还有在SpringSecurity框架中:

role和authorities的关系是:role是一种复杂的写法,有ROLE_前缀,authorities是role的简化写法

基于方法鉴权 在SpringSecurity6版本中@EnableGlobalMethodSecurity被弃用,取而代之的是 @EnableMethodSecurity。默认情况下,会激活pre-post注解,并在内部使用 AuthorizationManager。

新老API区别 此@EnableMethodSecurity替代了@EnableGlobalMethodSecurity。提供了以下改进: 1. 使用简化的AuthorizationManager。 2. 支持直接基于bean的配置,而不需要扩展GlobalMethodSecurityConfiguration 3. 使用Spring AOP构建,删除抽象并允许您使用Spring AOP构建块进行自定义 4. 检查是否存在冲突的注释,以确保明确的安全配置 5. 符合JSR-250 6. 默认情况下启用@PreAuthorize、@PostAuthorize、@PreFilter和@PostFilter

springboot3整合SpringSecurity实现登录校验与权限认证(万字超详细讲解)_第16张图片

主要的权衡似乎是您希望您的授权规则位于何处。重要的是要记住,当您使用基于注释的方法安全性 时,未注释的方法是不安全的。为了防止这种情况,请在HttpSecurity实例中声明一个兜底授权规则。 如果方法上也定义了权限,则会覆盖类上的权限

注意:使用注解的方式实现,如果接口的权限发生变化,需要修改代码了。

总结:

  1. 登录校验(Authentication):

    • 用户提交用户名和密码进行登录。
    • Spring Security会拦截登录请求,并将用户名和密码与存储在系统中的凭据(如数据库或LDAP)进行比对。
    • 如果用户名和密码匹配,则认为用户通过了身份验证,可以继续访问受限资源。
    • 认证成功后,Spring Security会创建一个包含用户信息和权限的安全上下文(Security Context)。
  2. 权限认证(Authorization):

    • 一旦用户通过了身份验证,Spring Security就会开始进行权限认证。
    • 针对每个受限资源或操作,可以配置相应的权限要求,例如需要哪些角色或权限才能访问。
    • Spring Security会根据配置的权限要求,检查当前用户所拥有的角色和权限,判断是否满足访问条件。
    • 如果用户拥有足够的角色或权限,就被允许访问资源;否则将被拒绝访问,并可能重定向到登录页面或返回相应的错误信息。

Spring Security通过身份验证(Authentication)来确认用户的身份,并通过授权(Authorization)来控制用户对受保护资源的访问。这种分离的设计使得安全配置更加灵活,并且可以轻松地对不同的用户和角色进行管理和控制。

你可能感兴趣的:(mysql,spring,boot)