springboot整合shiro实现基础的用户角色权限管理

文章目录

项目地址

1. 用户角色权限需要解决的问题及shiro的解决方案

  1. 灵活配置需要和不需要拦截的页面

shiro提供了过滤器可以灵活配置需要和不需要拦截的页面

  1. 实现用户认证

shiro整合了HttpSession(web应用)可以保存用户登录的信息。

  1. 根据当前认证用户加载不同的菜单权限

这个和shiro无关,直接展示用户具有的菜单权限即可

  1. 根据认证用户的权限,严格控制用户的操作

shiro可以用Realm的doGetAuthorizationInfo()方法做授权,结合shiro的注解(@RequiresPermissions(),@RequiresRoles())做权限控制

  1. 权限==>角色==>用户配置

可以灵活配置权限角色

2. 过滤器

public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //配置默认未登录跳转页面
        shiroFilterFactoryBean.setLoginUrl("/user/login");
        // 配置未授权跳转页面,filterChainDefinitionMap.put("/user/login2", "perms[2]")
        // 注解的未授权直接抛异常,由统一异常处理
        // shiroFilterFactoryBean.setUnauthorizedUrl("/user/unauthorized");

        // 拦截器.
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        // 配置不许认证的资源
        filterChainDefinitionMap.put("/static/**", "anon");
        filterChainDefinitionMap.put("/user/login", "anon");
        filterChainDefinitionMap.put("/user/tologin", "anon");
        // filterChainDefinitionMap.put("/user/login2", "perms[2]");

        // 配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了
        filterChainDefinitionMap.put("/user/loginout", "logout");

        // 除了上面所有,其它都需要授权,匹配方式,第一次匹配优先
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

3. 登录并获取菜单权限

@RequestMapping("/tologin")
@ResponseBody
public Map<String, Object> login(Model model, String userName, String password) {
    Map<String, Object> map = new HashedMap();
    if (StringUtils.isNotBlank(userName) && StringUtils.isNotBlank(password)) {
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(userName, password);
        token.setRememberMe(true);
        try {
            subject.login(token);
        } catch (AuthenticationException e) {
            e.printStackTrace();
            map.put("code", -1);
            map.put("msg", "登录失败, 用户名或密码错误");
            return map;
        }
        // 获取角色对应的权限
        List<SysPermission> sysPermissionList = sysPermissionService.selectSysPermissionsBySysUserName(userName);
        if (sysPermissionList != null && sysPermissionList.size() != 0) {
            model.addAttribute("sysPermissionList", sysPermissionList);
            model.addAttribute("isSysPermissionList", true);
        } else {
            model.addAttribute("isSysPermissionList", false);
        }
        // 当前登录用户
        SysUser sysUser = new SysUser();
        sysUser.setUserName(userName);
        model.addAttribute("sysUser", sysUserService.selectOne(sysUser));
        map.put("code", 1);
        return map;
    } else {
        map.put("code", -1);
        map.put("msg", "用户名与密码不能为空");
        return map;
    }
}

subject.login()最后由Realm的doGetAuthenticationInfo()执行,查询数据库获取用户,shiro自动帮我们比对密码是否正确

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    String userName = (String) token.getPrincipal();
    Example sysUserExample = new Example(SysUser.class);
    Example.Criteria criteria = sysUserExample.createCriteria();
    criteria.andEqualTo("userName", userName);
    SysUser user = sysUserMapper.selectByExample(sysUserExample).get(0);
    if (user != null) {
        AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(user.getUserName(), user.getPassword(), "xxx");
        return authcInfo;
    } else {
        return null;
    }
}

到此为止,网站就具有了认证和非认证的状态,但是仅仅有这两种状态不够,因为现在没法保证具体权限的控制,也就是说只要登录了就可以访问任何权限,需要解决这个问题还要利用shiro的授权控制。

4. shiro授权

shiro的授权操作是借助Realm的doGetAuthorizationInfo()来操作的,这个方法会获取用户对应的所有角色和权限,并且放入shiro中统一管理。

@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    String userName = (String) SecurityUtils.getSubject().getPrincipal();
    //根据用户名查询出用户记录
    Example sysUserExample = new Example(SysUser.class);
    Example.Criteria criteria = sysUserExample.createCriteria();
    criteria.andEqualTo("userName", userName);
    SysUser user = sysUserMapper.selectOneByExample(sysUserExample);

    SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    List<SysRole> roleList = sysRoleMapper.selectRolesByUserId(user.getId());

    Set<String> roles = new HashSet<>();
    if (roleList.size() > 0) {
        for (SysRole role : roleList) {
            roles.add(role.getRoleName());
            //根据角色id查询所有资源
            List<SysPermission> menuList = sysPermissionMapper.selectMenusByRoleId(role.getId());
            for (SysPermission menu : menuList) {
                info.addStringPermission(menu.getPermissionName()); // 添加权限
            }
        }
    }
    info.setRoles(roles);
    return info;
}

上述操作是把用户具有的所有角色权限加载出来了,但是还没设定具体的操作需要哪种权限,可以通过注解或代码的方式设定。

@RequestMapping("/usermanage")
@RequiresPermissions({"用户管理"})
public String userManage() {
    // 可以手动判断是否拥有权限,也可以通过注解配合ControllerAdvice
    //Subject subject = SecurityUtils.getSubject();
    //subject.isPermitted("用户管理");

    return "usermanage";
}

注:doGetAuthorizationInfo()方法是需要授权时,也就是解析注解或者执行授权代码时才执行的

5. 总结

上述只列出了大体的思路,详细的配置和实现有需要的请查看githup上的项目

项目地址

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值