SpringScurity+JWT实战讲解三(用户权限问题)

SpringScurity+JWT实战讲解三(用户权限问题),第1张

一:授权解释

关于权限部分,也是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,我们再来整体梳理一下授权、验证权限的流程:

  1. 用户登录或者调用接口时候识别到用户,并获取到用户的权限信息
  2. 注解标识Controller中的方法需要的权限或角色
  3. Security通过FilterSecurityInterceptor匹配URI和权限是否匹配
  4. 有权限则可以访问接口,当无权限的时候返回异常交给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();

    }
}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/942061.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-17
下一篇 2022-05-17

发表评论

登录后才能评论

评论列表(0条)

保存