在前面我们讲解了认证中所有常用配置,主要是对 http.formLogin() 和 http.logout() 进行操作。而在配置类中 http.authorizeHttpRequests() 主要是对 url 进行控制,也就是我们所说的授权(访问控制)。
注意:在 Spring Security6 之前,为 http.authorizeRequests() 方法。
下面示例将基于前面“Spring Security6 授权管理准备数工作”章节已经准备好了进行权限管理所需要的数据,来通过一个示例介绍权限管理的基本用法。
假设我们创建了一个名为 SysManagerController 的控制器,该控制器提供了如下功能:
获取系统信息 - /sys/get
新增系统 - /sys/add
更新系统信息 - /sys/update
删除系统信息 - /sys/delete
然而,这些功能仅“系统管理员”才能使用,即拥有“管理员”角色的用户。如果用户不是“管理员”,则不能访问这些功能。
创建 SysManagerController 控制器,该控制器提供的功能仅仅返回一个字符串,如下:
package com.hxstrive.spring_security.controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * 系统管理相关控制器 * @author hxstrive.com */ @RestController @RequestMapping("/sys") public class SysManagerController { @RequestMapping("/get") public String get(){ return "获取系统信息"; } @RequestMapping("/add") public String add(){ return "新增系统"; } @RequestMapping("/update") public String update(){ return "更新系统信息"; } @RequestMapping("/delete") public String delete(){ return "删除系统信息"; } }
在之前创建的自定义 UserDetailsService 接口实现类中,仅仅用来查询用户信息(用来验证用户名和密码是否正确),并没有添加任何权限。
下面将查询用户权限信息,并将权限信息添加到 UserDetails 中,如下:
package com.hxstrive.spring_security.config; import com.hxstrive.spring_security.service.UserService; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import java.util.List; /** * 自定义实现 UserDetailsService 接口 * @author hxstrive.com */ public class CustomUserDetailsService implements UserDetailsService { // 用户服务 private final UserService userService; public CustomUserDetailsService(UserService userService) { this.userService = userService; } @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { System.out.println("loadUserByUsername(String username) :: " + username); // 从数据库中加载用户信息 com.hxstrive.spring_security.model.UserModel user = userService.getByCount(username); if(null == user){ // 用户名不存在。抛出异常 throw new UsernameNotFoundException("用户名 [" + username + "] 不存在"); } // 看这里,查询用户㣐信息,包含角色和菜单 // 获取用户权限信息,返回一个字符串集合 List<String> authorities = userService.getUserAuthorities(user.getId()); // 创建 UserDetails 对象 return new User(username, user.getPassword(), AuthorityUtils.createAuthorityList(authorities)); } }
在 Spring Security 配置类中,添加 authorizeHttpRequests() 相关的配置信息,如下:
package com.hxstrive.spring_security.config; import com.hxstrive.spring_security.service.UserService; import jakarta.annotation.Resource; 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.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import static org.springframework.security.config.Customizer.withDefaults; /** * Spring Security 配置类 * @author hxstrive.com */ @Configuration public class SecurityConfig { @Resource private UserService userService; @Bean public UserDetailsService userDetailsService() { System.out.println("userDetailsService()"); return new CustomUserDetailsService(this.userService); } @Bean public PasswordEncoder passwordEncoder() { System.out.println("passwordEncoder()"); return new BCryptPasswordEncoder(); } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { System.out.println("securityFilterChain()"); // csrfCustomizer.ignoringRequestMatchers(*) 表示所有请求地址都不使用 csrf http.csrf(csrfCustomizer -> csrfCustomizer.ignoringRequestMatchers("*")) // authorize.anyRequest().authenticated() 表示所有的请求都需要进行身份验证。 .authorizeHttpRequests(authorize -> authorize.requestMatchers("/sys/*").hasRole("admin") .anyRequest().authenticated() ) // formLogin 用于配置表单登录功能。 // permitAll() 表示允许所有用户访问表单登录相关的页面,例如登录页面、登录处理接口等。 .formLogin(formLoginCustomizer -> formLoginCustomizer.loginPage("/view/login") // 设置登录页面为 /view/login .loginProcessingUrl("/login") // 设置登录处理接口为 /login .failureHandler((request, response, exception) -> { System.out.println("failureHandler()"); response.sendRedirect("/view/login?error"); }) // 设置登录成功自动跳转到 /success 页面,true 表示自动重定向到该页面 .successHandler((request, response, authentication) -> { System.out.println("successHandler()"); response.sendRedirect("/view/success"); }).permitAll() ) // 用于启用 HTTP 基本认证,withDefaults() 表示使用默认的 HTTP 基本认证配置。 .httpBasic(withDefaults()); return http.build(); } }
上述代码中,authorizeHttpRequests() 配置如下:
.authorizeHttpRequests(authorize -> authorize.requestMatchers("/sys/*").hasRole("admin") .anyRequest().authenticated() )
该配置表示所有以 /sys/ 开头的请求地址,必须拥有编码为“admin”的角色才能被访问。
重启项目,然后通过浏览器访问 http://localhost:8080/hello 地址,由于没有登录,将重定向到登录页面,如下图:
输入用户名(lisi)和密码(aaaaaa)登录,然后访问 http://localhost:8080/sys/get 地址,由于 lisi 用户没有系统管理员角色,因此无法访问,如下图:
如果使用(zhangsan)账号登录,即拥有管理员权限的账户,则可以正常访问,如下图:
注意,在后续章节中将省略掉控制器、启动类等代码展示,将直接给出最核心的配置信息,避免信息太多带来干扰。