授权
# 授权流程
在 FilterSecurityInterceptor中会从 SecurityContextHolder获取其中的 Authentication,然后获取其中的权限信息;
只需要把当前登录用户的权限信息存入 Authentication;
在SecurityConfig开启配置:
@EnableGlobalMethodSecurity(prePostEnabled = true)
1
需要权限的路径上加@PreAuthorize;
@RestController
public class HelloController {
@RequestMapping("/hello")
@PreAuthorize("hasAuthority('test')")
public String hello(){
return "hello";
}
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 封装权限信息
UserDetailsServiceImpl将权限信息封装到UserDetails中;
LoginUser
public class LoginUser implements UserDetails {
private User user;
//存储权限信息
private List<String> permissions;
public LoginUser(User user, List<String> permissions) {
this.user = user;
this.permissions = permissions;
}
//存储SpringSecurity所需要的权限信息的集合
@JSONField(serialize = false)
private List<GrantedAuthority> authorities;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
if(authorities!=null){
return authorities;
}
//把permissions中字符串类型的权限信息转换成GrantedAuthority对象存入authorities中
// authorities = new ArrayList<>();
// for (String permission : permissions) {
// SimpleGrantedAuthority authority = new SimpleGrantedAuthority(permission);
// authorities.add(authority);
// }
authorities = permissions.stream().
map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
return authorities;
}
//。。。。。。。
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
UserDetailsServiceImpl
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Autowired
private MenuMapper menuMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(User::getUserName,username);
User user = userMapper.selectOne(queryWrapper);
if(Objects.isNull(user)){
throw new RuntimeException("用户名密码错误");
}
List<String> permissionKeyList = menuMapper.selectPermsByUserId(user.getId());
return new LoginUser(user,permissionKeyList);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
JwtAuthenticationTokenFilter
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(loginUser,null,loginUser.getAuthorities());
1
2
2
# RBAC
查询用户权限sql:
SELECT
DISTINCT m.`perms`
FROM
sys_user_role ur
LEFT JOIN `sys_role` r ON ur.`role_id` = r.`id`
LEFT JOIN `sys_role_menu` rm ON ur.`role_id` = rm.`role_id`
LEFT JOIN `sys_menu` m ON m.`id` = rm.`menu_id`
WHERE
user_id = 1
AND r.`status` = 0
AND m.`status` = 0
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
mapper:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.rui.mapper.MenuMapper">
<select id="selectPermsByUserId" resultType="java.lang.String">
SELECT
DISTINCT m.`perms`
FROM
sys_user_role ur
LEFT JOIN `sys_role` r ON ur.`role_id` = r.`id`
LEFT JOIN `sys_role_menu` rm ON ur.`role_id` = rm.`role_id`
LEFT JOIN `sys_menu` m ON m.`id` = rm.`menu_id`
WHERE
user_id = #{userid}
AND r.`status` = 0
AND m.`status` = 0
</select>
</mapper>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 自定义失败处理
在认证或者授权的过程中出现了异常会被ExceptionTranslationFilter捕获到;
- 如果是认证过程中出现的异常会被封装成AuthenticationException然后调用AuthenticationEntryPoint对象的方法去进行异常处理。
- 如果是授权过程中出现的异常会被封装成AccessDeniedException然后调用AccessDeniedHandler对象的方法去进行异常处理。
@Component
public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
ResponseResult result = new ResponseResult(HttpStatus.FORBIDDEN.value(), "权限不足");
String json = JSON.toJSONString(result);
WebUtils.renderString(response,json);
}
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
@Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
ResponseResult result = new ResponseResult(HttpStatus.UNAUTHORIZED.value(), "认证失败请重新登录");
String json = JSON.toJSONString(result);
WebUtils.renderString(response,json);
}
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
配置类中配置:
@Autowired
private AuthenticationEntryPoint authenticationEntryPoint;
@Autowired
private AccessDeniedHandler accessDeniedHandler;
1
2
3
4
5
6
2
3
4
5
6
//异常处理
http
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.accessDeniedHandler(accessDeniedHandler);
1
2
3
4
5
2
3
4
5