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

目录

身份认证:

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

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

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

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

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

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

7、退出接口

权限校验:

1、基于请求:

2、基于方法:


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

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项目,并导入一些初始依赖:

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>2.0.21</version></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-test</artifactId><scope>test</scope></dependency>
</dependencies>

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

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

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

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等)

认证的实现流程:

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

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

实现UserDetailsService接口

@Component
public class MyUserDetailsService implements UserDetailsService {/**  UserDetailsService:提供查询用户功能,如根据用户名查询用户,并返回UserDetails*UserDetails,SpringSecurity定义的类, 记录用户信息,如用户名、密码、权限等* */@Autowiredprivate SysUserMapper sysUserMapper;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//根据用户名从数据库中查询用户SysUser sysUser = sysUserMapper.selectOne(new LambdaQueryWrapper<SysUser>().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,将来会再更改的@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return null;}public MySysUserDetails(SysUser sysUser) {this.id = sysUser.getId();this.username = sysUser.getUsername();this.password = sysUser.getPassword();}//    后面四个方法都是用户是否可用、是否过期之类的。我都设置为true@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}
}

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

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

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

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

@Configuration
@EnableWebSecurity //开启webSecurity服务
public class SecurityConfig {@Autowiredprivate 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 作为密码加密和解析器。* */
@Beanpublic 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层的方法:

在这之前,介绍一个非常重要的类:UsernamePasswordAuthenticationToken

UsernamePasswordAuthenticationToken是Spring Security中用于表示基于用户名和密码的身份验证令牌的类。它主要有以下两个构造方法:

  1. UsernamePasswordAuthenticationToken(Object principal, Object credentials)

    • principal参数表示认证主体,通常是用户名或用户对象。在身份验证过程中,这通常是用来标识用户的信息,可以是用户名、邮箱等。
    • credentials参数表示凭据,通常是用户的密码或其他凭证信息。在身份验证过程中,这用于验证用户的身份。
  2. UsernamePasswordAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities)

    • 除了上述两个参数外,这个构造方法还接受一个授权权限集合(authorities参数)。这个集合表示用户所拥有的权限,通常是一个包含用户权限信息的集合。
    • GrantedAuthority接口代表了用户的权限信息,可以通过该接口的实现类来表示用户具体的权限。

这两个构造方法的作用是创建一个包含用户身份信息、凭据信息和权限信息的身份验证令牌,以便在Spring Security中进行身份验证和授权操作。通过这些构造方法,可以将用户的相关信息封装成一个完整的身份验证对象,方便在安全框架中进行处理和验证。

总之,UsernamePasswordAuthenticationToken是在Spring Security中用于表示用户名密码身份验证信息的重要类,通过不同的构造方法可以满足不同场景下的需求。

  @Autowiredprivate AuthenticationManager authenticationManager;//    登录接口的具体实现@Overridepublic 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);
//返回tokenString 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接口,并且在用户表中导入了一条用户数据,那么,这里的用户名和密码就是我们在数据库中存储的用户名和密码)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8" xmlns:th="http://www.thymeleaf.org"><title>自定义的登录页面</title>
</head>
<body><form action="/sys-user/login" method="post">用户名: <input type="text" name="username" ><br>密码: <input type="password" name="password"><br>
<input type="submit" value="登录">
</form></body>
</html>

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

并且指定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)过滤器,指定我们自定义的登录表单路径,(解释一下fromLogin方法):

formLogin 方法是 Spring Security 中用于配置基于表单的登录认证的一种方式。它通常用于传统的 Web 应用程序,其中前端页面由后端动态生成,并且用户在页面中输入用户名和密码来进行登录。在这种情况下,Spring Security 负责处理登录请求、验证用户身份、生成会话等操作。

 /** 配置权限相关的配置* 安全框架本质上是一堆的过滤器,称之为过滤器链,每一个过滤器链的功能都不同* 设置一些链接不要拦截* */@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
//       关闭csrfhttpSecurity.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")
@ResponseBodypublic String index(){return "用户登录成功";
}
}

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

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

我们进行登录之后,会跳转到/test请求地址:

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

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

7、退出接口

需要注意的是在Spring Security中,没有专门用于处理退出失败的接口。退出(注销)操作通常是由浏览器发起的,Spring Security会拦截注销请求并执行相应的注销逻辑。

退出操作通常是通过调用SecurityContextLogoutHandler来完成的,它会清除用户的安全上下文,包括认证信息和会话信息。

在security框架中,默认提供了退出登陆的功能。请求地址是  /lohout  此为默认值,可以通过配置进行修该。直接请求 /logout ,会实现自动退出登录逻辑(默认的/logout接收get、和post请求)

退出登陆时,会清楚内存中的登录用户主体信息,销毁会话对象等等。

自定义退出接口:

httpSecurity.logout(logout->{logout.logoutUrl("/user/login")   //自定义退出接口.logoutSuccessHandler(logoutSuccess);  //退出成功之后的逻辑});

编写退出成功之后的逻辑,我们可以在这里删除掉redis中的数据,设置返回的信息等等....

@Component
public class LogoutSuccess implements LogoutSuccessHandler {@Resourceprivate RedisTemplate<String,String> redisTemplate;/*
* 登录成功之后的逻辑
* */@Overridepublic void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {String token = request.getHeader("token");//    删除redis中的数据redisTemplate.delete(token);Map<String,Object> map=new HashMap<>();map.put("msg","退出成功");map.put("code",200);response.getWriter().write(JSON.toJSONString(map));response.setContentType("application/json;charset=utf-8");}}

权限校验:

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

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

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

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

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

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

角色表:

权限表:

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

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

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

告知权限的流程:

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

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

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

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

@Component
public class MyUserDetailsService implements UserDetailsService {/**  UserDetailsService:提供查询用户功能,如根据用户名查询用户,并返回UserDetails*UserDetails,SpringSecurity定义的类, 记录用户信息,如用户名、密码、权限等* */@Autowiredprivate SysUserMapper sysUserMapper;@Autowired
private SysRoleMapper sysRoleMapper;
@Autowired
private SysPermissionsMapper sysPermissionsMapper;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//根据用户名从数据库中查询用户SysUser sysUser = sysUserMapper.selectOne(new LambdaQueryWrapper<SysUser>().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<SysRole>().eq(SysRole::getRoleName, "超级管理员"));Set<SysRole> roles=new HashSet<>();roles.add(sysRole);mySysUserDetails.setRoles(roles);SysPermissions sysPermissions = sysPermissionsMapper.selectById(1);Set<String> permissions=new HashSet<>();permissions.add(sysPermissions.getPermissionsName());mySysUserDetails.setPermissions(permissions);
}if ("lisi".equals(username)){
//lisi用户是普通管理员,拥有基本权限SysRole sysRole = sysRoleMapper.selectOne(new LambdaQueryWrapper<SysRole>().eq(SysRole::getRoleName, "普通管理员"));Set<SysRole> roles=new HashSet<>();roles.add(sysRole);mySysUserDetails.setRoles(roles);SysPermissions sysPermissions = sysPermissionsMapper.selectById(2);Set<String> permissions=new HashSet<>();permissions.add(sysPermissions.getPermissionsName());mySysUserDetails.setPermissions(permissions);}return mySysUserDetails;}
}

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

//    角色信息private Set<SysRole> roles;
//    权限信息private Set<String> permissions;//    用户拥有的权限集合,我这里先设置为null,将来会再更改的@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {System.err.println("进入权限的获取方法");List<GrantedAuthority> 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接口显示权限不够:

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

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<String> list = new ArrayList<>();
list.add("张三");
list.add("王麻子");
list.add("狗叫什么");return "一个test5请求";}
/*
* @PreFilter:过滤符合条件的数据返回,数据必须是Collection、map、Array【数组】
* */
@PreFilter("filterObject.length()>5")
@ResponseBody
@PostMapping("/test6")
public List<String> test6(@RequestBody List<String> 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

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

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

总结:

  1. 登录校验(Authentication):

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

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

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

这都是一些基础的理论,下面我将实战中演示使用security的案例:

前后端分离,使用vue3整合SpringSecurity加JWT实现登录认证_springsecurity整合vue3-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/44800.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【面试题】防火墙的部署模式有哪些?

防火墙的部署模式多种多样&#xff0c;每种模式都有其特定的应用场景和优缺点。以下是防火墙的主要部署模式&#xff1a; 一、按工作模式分类 路由模式 定义&#xff1a;当防火墙位于内部网络和外部网络之间时&#xff0c;需要将防火墙与内部网络、外部网络以及DMZ&#xff0…

事半功倍大法!财务数据API让企业工作智能化

在快速变化的商业环境中&#xff0c;财务管理的自动化已成为企业提升效率、降低成本和增强决策质量的关键。财务API&#xff0c;作为现代企业架构中不可或缺的一部分&#xff0c;提供了一种强大的工具&#xff0c;使得企业能够无缝集成各种财务服务和应用&#xff0c;实现数据的…

数据分析理论

数据分析的概念 数据分析是指通过恰当的统计方法和分析手段&#xff0c;对数据进行收集汇总&#xff0c;并进行加工处理。对处理过后的有效数据进行分析&#xff0c;发现存在的问题&#xff0c;制定可行的方案、从而帮助人们采取更科学的行动 数据分析4个层次 著名咨询公司Gart…

【WEB前端2024】3D智体编程:乔布斯3D纪念馆-第59-agent自动获取喵星人资讯并保存至云文件夹

【WEB前端2024】3D智体编程&#xff1a;乔布斯3D纪念馆-第59-agent自动获取喵星人资讯并保存至云文件夹 使用dtns.network德塔世界&#xff08;开源的智体世界引擎&#xff09;&#xff0c;策划和设计《乔布斯超大型的开源3D纪念馆》的系列教程。dtns.network是一款主要由Java…

Oracle执行一条SQL的内部过程

一、SQL语句根据其功能主要可以分为以下几大类&#xff1a; 1. 数据查询语言&#xff08;DQL, Data Query Language&#xff09; 功能&#xff1a;用于从数据库中检索数据&#xff0c;常用于查询表中的记录。基本结构&#xff1a;主要由SELECT子句、FROM子句、WHERE子句等组成…

stm32h743 阿波罗v2 NetXduo http server CubeIDE+CubeMX

在这边要设置mpu的大小&#xff0c;要用到http server&#xff0c;mpu得设置的大一些 我是这么设置的&#xff0c;做一个参考 同样&#xff0c;在FLASH.ld里面也要对应修改&#xff0c;SECTIONS里增加.tcp_sec和 .nx_data两个区&#xff0c;我们用ram_d2区域去做网络&#xff…

生产英特尔CPU处理器繁忙的一天

早晨&#xff1a;准备与检查 7:00 AM - 起床与准备 工厂员工们早早起床&#xff0c;快速洗漱并享用早餐。为了在一天的工作中保持高效&#xff0c;他们会进行一些晨间锻炼&#xff0c;保持头脑清醒和身体活力。 8:00 AM - 到达工厂 员工们到达英特尔的半导体制造工厂&#…

电脑拼图软件有哪些?盘点7种简单好用电脑拼图软件

如今我们无时无刻不使用着社交媒体&#xff0c;图片已经成为我们日常生活中不可或缺的一部分。无论是社交媒体分享、工作汇报还是个人创作&#xff0c;拼图软件都扮演着至关重要的角色。今天&#xff0c;就让我们一起来盘点7款电脑拼图软件&#xff0c;帮助你轻松找到最适合自己…

AI应用行业落地100例 | 全国首个司法审判垂直领域AI大模型落地深圳法院

《AI应用行业落地100例》专题汇集了人工智能技术在金融、医疗、教育、制造等多个关键行业中的100个实际应用案例&#xff0c;深入剖析了AI如何助力行业创新、提升效率&#xff0c;并预测了技术发展趋势&#xff0c;旨在为行业决策者和创新者提供宝贵的洞察和启发。 随着人工智能…

研究突破:无矩阵乘法的LLMs 计算!

通过在推理过程中使用优化的内核&#xff0c;内存消耗可以比未优化模型减少超过10倍。&#x1f92f; 该论文总结道&#xff0c;有可能创建第一个可扩展的无矩阵乘法LLM&#xff0c;在数十亿参数规模上实现与最先进的Transformer相媲美的性能。 另一篇最新论文《语言模型物理学…

14 学习总结:指针 · 其二 · 数组

目录 一、数组名的理解 &#xff08;一&#xff09;【数组名】与【&数组名[0]】 &#xff08;二&#xff09;区别于 【 sizeof(数组名) 】 和 【 &数组名 】 &#xff08;三&#xff09;总结 二、使用指针访问数组 三、一维数组传参的本质 四、冒泡排序 五、二…

PlugLink的技术架构实例解析(附源码)

在探讨PlugLink这一开源应用的实际应用与技术细节时&#xff0c;我们可以从其构建的几个核心方面入手&#xff0c;结合当前AI编程的发展趋势&#xff0c;为您提供既有实例又有深度解析的内容。 PlugLink的技术架构实例解析 前端技术选型 —— layui框架&#xff1a; PlugLi…

Windows桌面上透明的记事本怎么设置

作为一名经常需要记录灵感的作家&#xff0c;我的Windows桌面总是布满了各种文件和窗口。在这样的环境下&#xff0c;一个传统的记事本应用往往会显得突兀&#xff0c;遮挡住我急需查看的资料。于是&#xff0c;我开始寻找一种既能满足记录需求&#xff0c;又能保持桌面整洁美观…

画了一个简陋的曼德勃罗集

原文画了一个简陋的曼德勃罗集 - 知乎 (zhihu.com) 前两天看妈咪叔科普曼德勃罗集的视频&#xff1a; 【分形与混沌2】最有魅力的几何图形——曼德勃罗集与朱利亚集 天使与魔鬼共存_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili​www.bilibili.com/video/av79113074​编辑 虽然看过…

Dify中的工具

Dify中的工具分为内置工具&#xff08;硬编码&#xff09;和第三方工具&#xff08;OpenAPI Swagger/ChatGPT Plugin&#xff09;。工具可被Workflow&#xff08;工作流&#xff09;和Agent使用&#xff0c;当然Workflow也可被发布为工具&#xff0c;这样Workflow&#xff08;工…

java线程锁synchronized的几种情况

一、对象方法锁 1、成员方法加锁 同一个对象成员方法有3个synchronized修饰的方法&#xff0c;通过睡眠模拟业务操作 public class CaseOne {public synchronized void m1(){try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace()…

ChIP项目文章CMI(IF=24.1)|IRF1激活可促进辐射诱导的细胞死亡和炎症反应

2024年6月7日&#xff0c;四川大学张舒羽教授团队在Cellular & Molecular Immunology&#xff08;IF24.1&#xff09;期刊上发表了题为“Chaperone-and PTM-mediated activation of IRF1 tames radiation-induced cell death and inflammation response”的文章&#xff0c…

Flexcel学习笔记

1.引用的单元 FlexCel.Core 始终需要使用的一个单元。 多系统运行时。{$IFDEF LINUX}SKIA.FlexCel.Core{$ELSE}{$IFDEF FIREMONKEY}FMX.FlexCel.Core{ $ELSE}VCL.FlexCel.Core{$ENDIF}{$ENDIF} FlexCel.XlsAdapter这是FlexCel的xls/x引擎。如果您正在处理xls或xlsx文件&#x…

搭建邮局服务器的配置步骤?如何管理协议?

搭建邮局服务器需要考虑的安全措施&#xff1f;怎么搭建服务器&#xff1f; 在现代互联网环境中&#xff0c;电子邮件是重要的沟通工具。为了保证信息传递的稳定性和安全性&#xff0c;许多企业选择自行搭建邮局服务器。AokSend将详细介绍搭建邮局服务器的配置步骤&#xff0c…

parquet介绍

概述 Apache Parquet 是一种开源的列式数据文件格式&#xff0c;旨在实现高效的数据存储和检索。它提供高性能压缩和编码方案(encoding schemes)来批量处理复杂数据&#xff0c;并且受到许多编程语言和分析工具的支持。 parquet-format parquet-format 存储库托管 Apache Pa…