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项目,并导入一些初始依赖: org.springframework.boot spring-boot-starter-security org.springframework.boot spring-boot-starter-web com.alibaba fastjson 2.0.21<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>
1234567891011121314151617181920 2、由于我们加入了 spring-boot-starter-security 的依赖,所以security就会自动生效了。这时直接编写一个controller控制器,并编写一个接口进行测试:可以看到我们在访问这个接口时出现了拦截,必须要我们进行登录之后才能访问;
那么接下来我们就来实现第一个功能:用户登录认证;
3、自定义用户的登录认证:Spring Security 6.x 的认证实现流程如下:
用户提交登录请求Spring Security 将请求交给 UsernamePasswordAuthenticationFilter 过滤器处理。UsernamePasswordAuthenticationFilter 获取请求中的用户名和密码,并生成一个 AuthenticationToken 对象,将其交给 AuthenticationManager 进行认证。AuthenticationManager 通过 UserDetailsService 获取用户信息,然后使用 PasswordEncoder 对用户密码进行校验。如果密码正确,AuthenticationManager 会生成一个认证通过的 Authentication 对象,并返回给 UsernamePasswordAuthenticationFilter 过滤器。如果密码不正确,则 AuthenticationManager 抛出一个 AuthenticationException 异常。UsernamePasswordAuthenticationFilter 将 Authentication 对象交给 SecurityContextHolder 进行管理,并调用 AuthenticationSuccessHandler 处理认证成功的情况。如果认证失败,UsernamePasswordAuthenticationFilter 会调用 AuthenticationFailureHandler 处理认证失败的情况。看起来有点复杂,其实写起来很简单的。spring security的底层就是一堆的过滤器来是实现的,而我们只需要编写一些重要的过滤器即可,其他的就用spring security默认的实现,只要不影响我们正常的登录功能即可。
(创建一个用户表用来进行登录实现,注意这个表中的用户名不能重复,我们将用户名作为每一个用户的唯一凭证,就如同人的手机号或者身份证号一样)表的结构非常简单,一些配置我这里就不在描述了(实体类、mapper、service、controller等)
认证的实现流程:
1、创建一个MyUserDetailsService类来实现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 { 12345678
//根据用户名从数据库中查询用户
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; 12345
// 用户拥有的权限集合,我这里先设置为null,将来会再更改的
@Override
public Collection< extends GrantedAuthority> 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; }
1234567891011121314151617181920212223242526}
2、通过配置类对AuthenticationManager与自定义的UserDetails和PasswordEncoder进行关联
Spring Security是通过AuthenticationManager实现的认证,会借此来判断用户名和密码的正确性
密码解析器spring security框架定义的接口:PasswordEncoder
spring security框架强制要求,必须在spring容器中存在PasswordEncoder类型对象,且对象唯一
@Configuration
@EnableWebSecurity //开启webSecurity服务
public class SecurityConfig {
@Autowired private MyUserDetailsService myUserDetailsService; 12
@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; } 123
对应的service层的方法:
在这之前,介绍一个非常重要的类:UsernamePasswordAuthenticationToken
**UsernamePasswordAuthenticationToken**是Spring Security中用于表示基于用户名和密码的身份验证令牌的类。它主要有以下两个构造方法:
UsernamePasswordAuthenticationToken(Object principal, Object credentials)
**principal**参数表示认证主体,通常是用户名或用户对象。在身份验证过程中,这通常是用来标识用户的信息,可以是用户名、邮箱等。**credentials**参数表示凭据,通常是用户的密码或其他凭证信息。在身份验证过程中,这用于验证用户的身份。UsernamePasswordAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities)
除了上述两个参数外,这个构造方法还接受一个授权权限集合(**authorities**参数)。这个集合表示用户所拥有的权限,通常是一个包含用户权限信息的集合。**GrantedAuthority**接口代表了用户的权限信息,可以通过该接口的实现类来表示用户具体的权限。这两个构造方法的作用是创建一个包含用户身份信息、凭据信息和权限信息的身份验证令牌,以便在Spring Security中进行身份验证和授权操作。通过这些构造方法,可以将用户的相关信息封装成一个完整的身份验证对象,方便在安全框架中进行处理和验证。
总之,**UsernamePasswordAuthenticationToken**是在Spring Security中用于表示用户名密码身份验证信息的重要类,通过不同的构造方法可以满足不同场景下的需求。
编写具体的登录方法,创建一个UsernamePasswordAuthenticationToken对象,并传入相应的用户名和密码;注入一个AuthenticationManager的bean,这个bean是spring security封装的用来进行认证的类,调用这个类的authenticate方法并传入UsernamePasswordAuthenticationToken对象;
@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接口,并且在用户表中导入了一条用户数据,那么,这里的用户名和密码就是我们在数据库中存储的用户名和密码)
登录成功之后,我们就可以访问到test的信息了:
既然这个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(); } 12 5、将项目运行起来(我同时还写了一个普通的test方法,类型是get,没有放行,用于测试能不能拦截到):
访问test请求:遇到拦截,说明我们的配置生效了
访问登录页面:能正常访问,且密码正确,返回了一个我们自己生成的一个token。
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)过滤器,指定我们自定义的登录表单路径,(解释一下fromLogin方法):
formLogin 方法是 Spring Security 中用于配置基于表单的登录认证的一种方式。它通常用于传统的 Web 应用程序,其中前端页面由后端动态生成,并且用户在页面中输入用户名和密码来进行登录。在这种情况下,Spring Security 负责处理登录请求、验证用户身份、生成会话等操作。
/*
* 配置权限相关的配置
* 安全框架本质上是一堆的过滤器,称之为过滤器链,每一个过滤器链的功能都不同
* 设置一些链接不要拦截
* */
@Bean public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception { 12
// 关闭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(); } 12
注意,这里还需要将/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地址,这个地址没有放行,而且我们这是没有登录,那么会自动跳转到我们自定义的登录页面:
我们进行登录之后,会跳转到/test请求地址:
可以看到我们的结果与我们设想的一样:
现在我们直接访问/login登录页面:可以看到返回了/index页面的内容(这个是我们设置的默认登录成功之后返回的页面)
7、退出接口需要注意的是在Spring Security中,没有专门用于处理退出失败的接口。退出(注销)操作通常是由浏览器发起的,Spring Security会拦截注销请求并执行相应的注销逻辑。
退出操作通常是通过调用**SecurityContextLogoutHandler**来完成的,它会清除用户的安全上下文,包括认证信息和会话信息。
在security框架中,默认提供了退出登陆的功能。请求地址是 /lohout 此为默认值,可以通过配置进行修该。直接请求 /logout ,会实现自动退出登录逻辑(默认的/logout接收get、和post请求)
退出登陆时,会清楚内存中的登录用户主体信息,销毁会话对象等等。
自定义退出接口:
httpSecurity.logout(logout->{ logout.logoutUrl("/user/login") //自定义退出接口 .logoutSuccessHandler(logoutSuccess); //退出成功之后的逻辑 }); 12345
编写退出成功之后的逻辑,我们可以在这里删除掉redis中的数据,清除登录的上下文,设置返回的信息等等…(如果是前后端分离状态下的spring security,这些工作都可以在自定义的退出接口中进行实现。如果是前后端不分离的表单式登录,还是使用传统的Cookie和Session来进行用户信息的保存,我们自需要调用ogout.logoutUrl(“/user/login”) 方法来指定退出路径即可,退出的逻辑不需要我们来实现。)
@Component public class LogoutSuccess implements LogoutSuccessHandler { @Resource private RedisTemplate<String,String> redisTemplate; /* * 登录成功之后的逻辑 * */ @Override public 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"); } }
123456789101112131415161718192021222324权限校验:
我们费了很多功夫完成了身份认证,权限校验相对来说是比较简单的:
首先,我先解释一下角色与权限在SpringSecurity中的作用:
角色(Role):角色是一组权限的集合,通常代表着用户的身份或职责。在Spring Security中,可以通过配置将角色分配给用户或者用户组,以此来控制用户对系统资源的访问。例如,管理员拥有添加、删除和修改用户的权限,而普通用户只能查看自己的信息。
权限(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定义的类, 记录用户信息,如用户名、密码、权限等 \* \*/ @Autowired private SysUserMapper sysUserMapper; 123456
@Autowired
private SysRoleMapper sysRoleMapper;
@Autowired
private SysPermissionsMapper sysPermissionsMapper;
@Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 12
//根据用户名从数据库中查询用户
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)){ 1
//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; } 12
}
在实现了UserDetailes接口的用户信息类MySysUserDetails中完成角色和权限的赋值:
// 角色信息
private Set roles;
// 权限信息
private Set permissions;
// 用户拥有的权限集合,我这里先设置为null,将来会再更改的
@Override
public Collection< extends GrantedAuthority> getAuthorities() {
System.err.println(“进入权限的获取方法”);
List<GrantedAuthority> authorities = new ArrayList<>(); // 授权信息列表 1
// 将角色名称添加到授权信息列表中
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请求"; 1
}
/*
@PostAuthorize:在方法返回时进行校验。
可以还是校验权限、或者校验一些其他的东西(接下来我们校验返回值的长度)
*返回结果的长度大于3、则认为是合法的
returnObject:固定写法,代指返回对象
* */
@ResponseBody
@PostAuthorize(“returnObject.length()>4”)
@GetMapping(“/test4”)
public String test4(){
System.out.println(“一个test4请求”);
return "小张自傲张最终"; 1
}
/\* 1
* @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请求"; } 12
/*
* @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
主要的权衡似乎是您希望您的授权规则位于何处。重要的是要记住,当您使用基于注释的方法安全性 时,未注释的方法是不安全的。为了防止这种情况,请在HttpSecurity实例中声明一个兜底授权规则。 如果方法上也定义了权限,则会覆盖类上的权限
注意:使用注解的方式实现,如果接口的权限发生变化,需要修改代码了。
总结:
登录校验(Authentication):
用户提交用户名和密码进行登录。Spring Security会拦截登录请求,并将用户名和密码与存储在系统中的凭据(如数据库或LDAP)进行比对。如果用户名和密码匹配,则认为用户通过了身份验证,可以继续访问受限资源。认证成功后,Spring Security会创建一个包含用户信息和权限的安全上下文(Security Context)。权限认证(Authorization):
一旦用户通过了身份验证,Spring Security就会开始进行权限认证。针对每个受限资源或操作,可以配置相应的权限要求,例如需要哪些角色或权限才能访问。Spring Security会根据配置的权限要求,检查当前用户所拥有的角色和权限,判断是否满足访问条件。如果用户拥有足够的角色或权限,就被允许访问资源;否则将被拒绝访问,并可能重定向到登录页面或返回相应的错误信息。Spring Security通过身份验证(Authentication)来确认用户的身份,并通过授权(Authorization)来控制用户对受保护资源的访问。这种分离的设计使得安全配置更加灵活,并且可以轻松地对不同的用户和角色进行管理和控制。
这都是一些基础的理论,只是一个小demo,带你认识一下spring security的工作原理,下面我将实战中演示使用security的案例:
前后端分离,使用vue3整合SpringSecurity加JWT实现登录认证_springsecurity整合vue3-CSDN博客
创作不易,如果觉得这篇文章帮助到了你,请给博主点个赞。
网址:springboot3整合SpringSecurity实现登录校验与权限认证(万字超详细讲解) https://www.yuejiaxmz.com/news/view/736672
相关内容
基于java微信小程序的校园二手商城设计与实现计算机毕业设计实现流程,看这篇就够了(1.2w字超详细流程)
Python实现校园网自动登录的脚本分享
基于SpringBoot+Vue的大学校园旧物捐赠网站的详细设计和实现(源码+lw+部署文档+讲解等)
基于微信小程序的校园二手图书交易小程序设计与实现(源码+lw+部署+讲解)
校园智能安防信息化系统的设计与实现
基于SpringBoot+Vue+uniapp的个性化美食推荐系统的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue的智能健康饮食系统的设计与实现(源码+lw+部署+讲解)
“打官司=打证据” 证据详解及18类案件所需证据明细(赶紧收藏)
WPS会员登录限制:最多可登几台电脑?