关于权限部分,也是security的重要功能,当用户认证成功之后,我们就知道谁在访问系统接口,这是又有一个问题,就是这个用户有没有权限来访问我们这个接口呢,要解决这个问题,我们需要知道用户有哪些权限,哪些角色,这样security才能我们做权限判断。
之前我们已经定义及几张表,用户、角色、菜单、以及一些关联表,一般当权限粒度比较细的时候,我们都通过判断用户有没有此菜单或 *** 作的权限,而不是通过角色判断,而用户和菜单是不直接做关联的,是通过用户拥有哪些角色,然后角色拥有哪些菜单权限这样来获得的。
问题1:我们是在哪里赋予用户权限的?有两个地方:
1、用户登录,调用调用UserDetailsService.loadUserByUsername()方法时候可以返回用户的权限信息。
2、接口调用进行身份认证过滤器时候JWTAuthenticationFilter,需要返回用户权限信息
问题2:在哪里决定什么接口需要什么权限?
Security内置的权限注解:
@PreAuthorize:方法执行前进行权限检查
@PostAuthorize:方法执行后进行权限检查
@Secured:类似于 @PreAuthorize
可以在Controller的方法前添加这些注解表示接口需要什么权限。
比如需要Admin角色权限:
@PreAuthorize("hasRole('admin')")
比如需要添加管理员的 *** 作权限
@PreAuthorize("hasAuthority('sys:user:save')")
ok,我们再来整体梳理一下授权、验证权限的流程:
- 用户登录或者调用接口时候识别到用户,并获取到用户的权限信息
- 注解标识Controller中的方法需要的权限或角色
- Security通过FilterSecurityInterceptor匹配URI和权限是否匹配
- 有权限则可以访问接口,当无权限的时候返回异常交给AccessDeniedHandler *** 作类处理
ok,流程清晰之后我们就开始我们的编码:
二:解决用户权限1.在JwtAuthenticationFilter将用户的权限赋给security
用户登录,调用调用UserDetailsService.loadUserByUsername()方法时候可以返回用户的权限信息
UserService.java
public interface UserService extends IService<User> {
User getByUsername(String username);
String getAuthorityById(Long userId);
}
UserImplService.java
@Service
public class UserImplService extends ServiceImpl<UserMapper, User> implements UserService {
@Autowired
RoleService roleService;
@Autowired
UserMapper userMapper;
@Autowired
MenuService menuService;
@Override
public User getByUsername(String username) {
return getOne(new QueryWrapper<User>().eq("username",username));
}
@Override
public String getAuthorityById(Long userId) {
String authority = "";
// 获取角色
List<Role> roles = roleService.list(
new QueryWrapper<Role>().inSql("id", "select role_id from sys_suer_role where user_id = " + userId));
if(roles.size() > 0){
String rolesCodes = roles.stream().map(r -> "ROLE_" + r.getCode()).collect(Collectors.joining(","));
authority = rolesCodes;
}
// 获取菜单 *** 作编码
List<Long> menusIds = userMapper.getNavMenuIds(userId);
if(menusIds.size() > 0){
List<Menu> menus = menuService.listByIds(menusIds);
String menusPerms = menus.stream().map(m -> m.getPerms()).collect(Collectors.joining(","));
authority = authority.concat(",").concat(menusPerms);
}
return authority;
}
}
UserMapper.java
@Repository
public interface UserMapper extends BaseMapper<User> {
List<Long> getNavMenuIds(Long userId);
}
UserMapper.xml
<select id="getNavMenuIds" resultType="java.lang.Long">
SELECT DISTINCT menu_id FROM sys_user_role ur LEFT JOIN sys_role_menu rm ON rm.role_id = ur.role_id
WHERE user_id = #{user_id}
</select>
可以使用postman进行测试
在接口的请求头中加上Authorization
1.添加缓存
为了避免每次访问都要查询数据库,将用户的authority储存到redis中
/**
* 获取当前用户的权限
* @param userId
* @return
*/
@Override
public String getAuthorityById(Long userId) {
User user = userMapper.selectById(userId);
// ROLE_admin,ROLE_normal,sys:user:list,....
String authority = "";
if(redisUtil.hasKey("GrantedAuthority"+user.getUsername())){
authority = (String) redisUtil.get("GrantedAuthority" + user.getUsername());
}
else{
// 获取角色
List<Role> roles = roleService.list(
new QueryWrapper<Role>().inSql("id", "select role_id from sys_user_role where user_id = " + userId));
if(roles.size() > 0){
String rolesCodes = roles.stream().map(r -> "ROLE_" + r.getCode()).collect(Collectors.joining(","));
authority = rolesCodes.concat(",");
}
// 获取菜单 *** 作编码
List<Long> menusIds = userMapper.getNavMenuIds(userId);
if(menusIds.size() > 0){
List<Menu> menus = menuService.listByIds(menusIds);
String menusPerms = menus.stream().map(m -> m.getPerms()).collect(Collectors.joining(","));
authority = authority.concat(menusPerms);
}
redisUtil.set("GrantedAuthority"+user.getUsername(),authority,60*60);
}
return authority;
}
2.用户在前端进行权限的额修改时,删除redis的缓存
@Service
public class UserImplService extends ServiceImpl<UserMapper, User> implements UserService {
@Autowired
RoleService roleService;
@Autowired
UserMapper userMapper;
@Autowired
RedisUtil redisUtil;
@Autowired
MenuService menuService;
@Override
public User getByUsername(String username) {
return getOne(new QueryWrapper<User>().eq("username",username));
}
/**
* 获取当前用户的权限
* @param userId
* @return
*/
@Override
public String getAuthorityById(Long userId) {
User user = userMapper.selectById(userId);
// ROLE_admin,ROLE_normal,sys:user:list,....
String authority = "";
if(redisUtil.hasKey("GrantedAuthority"+user.getUsername())){
authority = (String) redisUtil.get("GrantedAuthority" + user.getUsername());
}
else{
// 获取角色
List<Role> roles = roleService.list(
new QueryWrapper<Role>().inSql("id", "select role_id from sys_user_role where user_id = " + userId));
if(roles.size() > 0){
String rolesCodes = roles.stream().map(r -> "ROLE_" + r.getCode()).collect(Collectors.joining(","));
authority = rolesCodes.concat(",");
}
// 获取菜单 *** 作编码
List<Long> menusIds = userMapper.getNavMenuIds(userId);
if(menusIds.size() > 0){
List<Menu> menus = menuService.listByIds(menusIds);
String menusPerms = menus.stream().map(m -> m.getPerms()).collect(Collectors.joining(","));
authority = authority.concat(menusPerms);
}
redisUtil.set("GrantedAuthority"+user.getUsername(),authority,60*60);
}
return authority;
}
/**
* 用户的权限有变化时,清除redis中的权限缓存
* @param username
*/
@Override
public void clearAuthorityInfo(String username) {
redisUtil.del("GrantedAuthority"+username);
}
/**
* 用户的角色发生变化时,清除redis中的权限缓存
* @param roleId
*/
@Override
public void clearAuthorityInfoByRoleId(Long roleId) {
List<User> userList = this.list(new QueryWrapper<User>()
.inSql("id", "SELECT user_id FROM sys_user_role where role_id =" + roleId));
userList.forEach(u ->{
this.clearAuthorityInfo(u.getUsername());
});
}
/**
* 用户的 *** 作权限发生变化时,清除redis中的权限缓存
* @param menuId
*/
@Override
public void clearAuthorityInfoByMenuId(Long menuId) {
List<String> userIds = userMapper.getUserIds(menuId);
userIds.forEach(u -> {
this.clearAuthorityInfo(u);
});
}
}
四:用户退出
实现LogoutSuccessHandler ,清除jwt
@Component
public class JwtLogoutSuccessHandler implements LogoutSuccessHandler {
@Autowired
JwtUtils jwtUtils;
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
if(authentication != null){
new SecurityContextLogoutHandler().logout(request,response,authentication);
}
response.setContentType("application/json;charset=UTF-8");
ServletOutputStream outputStream = response.getOutputStream();
response.setHeader(jwtUtils.getHeader(), "");
Result result = Result.succ("");
outputStream.write(JSONUtil.toJsonStr(result).getBytes("UTF-8"));
outputStream.flush();
outputStream.close();
}
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)