文章目录
-
- 一、基于角色的请求控制
- 二、加载用户角色信息
- 三、角色与资源的关联
- 四、测试角色权限控制
-
- [1. 未登录用户访问受保护资源](#1. 未登录用户访问受保护资源)
- [2. 登录用户访问受保护资源](#2. 登录用户访问受保护资源)
- [3. 角色不足的用户访问受保护资源(把前面改成.roles("USER"))](#3. 角色不足的用户访问受保护资源(把前面改成.roles("USER")))
- 五、自定义异常处理
-
- [1. 自定义未授权处理器](#1. 自定义未授权处理器)
- [2. 注册自定义异常处理器](#2. 注册自定义异常处理器)
- 六、总结
在Web应用的开发中,权限管理是必不可少的一环。继上次分享了基于 用户-权限-资源 的权限控制后,这次我们来探讨如何通过Spring Security实现 用户-角色-资源 的权限管理。
一、基于角色的请求控制
首先,我们需要根据不同的资源路径设置相应的角色要求。以下是安全配置的代码:
java
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// 开启授权保护
http.authorizeHttpRequests(authorize -> authorize
// 所有 /user/** 路径下的请求需要 ADMIN 角色
.requestMatchers("/user/**").hasRole("ADMIN")
// 对所有请求开启授权保护
.anyRequest()
// 已认证的请求自动被授权
.authenticated());
// 其他配置省略...
}
在这段代码中,我们通过hasRole("ADMIN")
方法,指定访问/user/**
路径的请求需要具备ADMIN
角色。这样,我们就实现了基于角色的资源访问控制。
二、加载用户角色信息
接下来,我们需要为用户加载其对应的角色信息。在Spring Security中,可以通过实现UserDetailsService
接口的loadUserByUsername
方法:
java
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(User::getUsername, username);
User user = userMapper.selectOne(lambdaQueryWrapper);
if (user == null) {
throw new UsernameNotFoundException(username);
} else {
return org.springframework.security.core.userdetails.User.withUsername(user.getUsername())
.password(user.getPassword())
.disabled(!user.getEnabled())
// 设置用户的凭据是否过期,false 表示凭据未过期
.credentialsExpired(false)
// 设置账户是否被锁定,false 表示账户未锁定
.accountLocked(false)
// 为用户分配 ADMIN 角色
.roles("ADMIN")
.build();
}
}
在这个方法中,我们:
- 从数据库中根据用户名查询用户信息。
- 如果用户存在,使用
User.withUsername()
方法构建一个UserDetails
对象。 - 通过
.roles("ADMIN")
方法为用户分配ADMIN
角色。
需要注意的是,Spring Security在处理角色时,会自动为角色名添加"ROLE_"
前缀。因此,"ADMIN"
角色实际上对应权限"ROLE_ADMIN"
。
三、角色与资源的关联
通过上述配置,我们实现了以下功能:
- 用户:通过数据库加载,包含用户名、密码、是否启用等信息。
- 角色 :为用户分配特定的角色,例如
ADMIN
。 - 资源:指定哪些URL路径需要哪些角色才能访问。
当用户尝试访问受保护的资源时,Spring Security会根据用户的角色信息决定是否授权。
四、测试角色权限控制
以下是一些测试场景,帮助验证我们的权限控制是否正确。
1. 未登录用户访问受保护资源
当未登录的用户尝试访问/user/list
时,会被重定向到登录页面,提示需要先进行身份认证。
2. 登录用户访问受保护资源
使用具备ADMIN
角色的用户登录后,尝试访问/user/list
,应当能够正常访问。
3. 角色不足的用户访问受保护资源(把前面改成.roles("USER"))
如果登录的用户没有ADMIN
角色,尝试访问/user/list
时,会收到权限不足的提示,无法访问该资源。
五、自定义异常处理
为了提升用户体验,我们可以自定义异常处理器,返回统一的JSON格式错误信息。
1. 自定义未授权处理器
java
public class MyAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException, ServletException {
// 创建结果对象
HashMap<String, Object> result = new HashMap<>();
result.put("code", -1);
result.put("message", "没有访问该资源的权限");
// 转换成JSON字符串
String json = JSON.toJSONString(result);
// 返回响应
response.setContentType("application/json;charset=utf-8");
response.getWriter().println(json);
}
}
2. 注册自定义异常处理器
java
http.exceptionHandling(exception -> {
// 请求未认证的处理
exception.authenticationEntryPoint(new MyAuthenticationEntryPoint());
// 请求未授权的处理
exception.accessDeniedHandler(new MyAccessDeniedHandler());
});
通过上述配置,当用户没有权限访问某个资源时,会返回自定义的JSON错误信息,方便前端进行处理。
六、总结
通过以上步骤,我们实现了基于用户-角色-资源的权限控制:
- 用户 :通过
UserDetailsService
加载用户信息,并为其分配ADMIN
角色。 - 角色 :使用
.roles("ADMIN")
方法为用户指定角色。 - 资源 :通过
requestMatchers("/user/**").hasRole("ADMIN")
指定需要ADMIN
角色才能访问的资源。
这种方式的优势在于:
- 管理方便:角色可以包含多个权限,简化了权限管理的复杂度。
- 灵活性高:可以根据需要动态调整用户的角色,进而控制其访问权限。
在实际项目中,通常会有一个角色和权限的对应关系表,角色下包含多个权限,用户可以被分配一个或多个角色。
希望这篇文章能对大家在权限管理的实践中有所帮助。如果有任何疑问或建议,欢迎在评论区留言讨论!