spring-shiro权限控制realm实战教程

 更新时间:2021年10月15日 16:00  点击:1739

spring-shiro权限控制realm

用户与角色实体

Role.java

@Data
@Entity
public class Role {
    @Id
    @GeneratedValue
    private Integer id;
    private Long userId;
    private String role;
}

User.java

@Data
@Entity
public class User {
    @Id
    @GeneratedValue
    private Long id;
    private String username;
    private String password;
}

Realm类

首先建立 Realm 类,继承自 AuthorizingRealm,自定义我们自己的授权和认证的方法。Realm 是可以访问特定于应用程序的安全性数据(如用户,角色和权限)的组件。

Realm.java

public class Realm extends AuthorizingRealm {
    @Autowired
    private UserService userService;
    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //从凭证中获得用户名
        String username = (String) SecurityUtils.getSubject().getPrincipal();
        //根据用户名查询用户对象
        User user = userService.getUserByUserName(username);
        //查询用户拥有的角色
        List<Role> list = roleService.findByUserId(user.getId());
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        for (Role role : list) {
            //赋予用户角色
            info.addStringPermission(role.getRole());
        }
        return info;
    }
    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //获得当前用户的用户名
        String username = (String) authenticationToken.getPrincipal();
        //从数据库中根据用户名查找用户
        User user = userService.getUserByUserName(username);
        if (userService.getUserByUserName(username) == null) {
            throw new UnknownAccountException(
                    "没有在本系统中找到对应的用户信息。");
        }
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(),getName());
        return info;
    }
}

Shiro 配置类

ShiroConfig.java

@Configuration
public class ShiroConfig {
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        //以下是过滤链,按顺序过滤,所以/**需要放最后
        //开放的静态资源
        filterChainDefinitionMap.put("/favicon.ico", "anon");//网站图标
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }
    @Bean
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager(myRealm());
        return defaultWebSecurityManager;
    }
    @Bean
    public MyRealm myRealm() {
        MyRealm myRealm = new MyRealm();
        return myRealm;
    }
}

控制器

UserController.java

@Controller
public class UserController {
    @Autowired
    private UserService userService;
    @GetMapping("/")
    public String index() {
        return "index";
    }
    @GetMapping("/login")
    public String toLogin() {
        return "login";
    }
    @GetMapping("/admin")
    public String admin() {
        return "admin";
    }
    @PostMapping("/login")
    public String doLogin(String username, String password) {
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        Subject subject = SecurityUtils.getSubject();
        try {
            subject.login(token);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "redirect:admin";
    }
    @GetMapping("/home")
    public String home() {
        Subject subject = SecurityUtils.getSubject();
        try {
            subject.checkPermission("admin");
        } catch (UnauthorizedException exception) {
            System.out.println("没有足够的权限");
        }
        return "home";
    }
    @GetMapping("/logout")
    public String logout() {
        return "index";
    }
}

Service

UserService.java

@Service
public class UserService {
    @Autowired
    private UserDao userDao;
    public User getUserByUserName(String username) {
        return userDao.findByUsername(username);
    }
    @RequiresRoles("admin")
    public void send() {
        System.out.println("我现在拥有角色admin,可以执行本条语句");
    }
}

shiro权限不生效原因分析

shiro遇到的坑

-项目中使用shiro做登录校验和权限管理,在配置权限时遇到小坑,记录一下。

  • 环境:springboot+freemarker+shiro
  • 场景:后台管理,配置菜单以及按钮权限,分为三个层级,一二级暂时只考虑是否查看权限,第三层级为页面按钮权限,分增删改查。详情看图
  • 问题:一二层级正常,第三层级权限不起作用!

权限标签定义如下:

标签定义 页面一 页面二
第一层级 one:view two:view
第二层级 one:page1:view two:page2:view
第三层级 one:page1:view:add two:page2:view:add

开始怀疑是数据库没有录入,查看后权限标签与角色已对应,排除。

后面怀疑是页面问题,后面把第三层级标签与第一二层级同一页面,依然不起作用,排除。

后面怀疑是权限标签定义问题,把第三层级标签改为one:page1:data:add,奇迹出现,权限生效。证实权限标签定义出了问题。

问题原因:权限标签定义问题

但是后来想想为什么会出现这种问题,每个标签都是独一无二的,对此我对shiro对于权限标签的校验产生了兴趣,查看源码,一路debug后最终在org.apache.shiro.authz.permission中看到了关键所在,核心代码如下

//当这个方法返回true时说明有此权限
//这个p是代表当前循环匹配到的权限标签
public boolean implies(Permission p) {
// By default only supports comparisons with other WildcardPermissions
if (!(p instanceof WildcardPermission)) {
return false;
}
    WildcardPermission wp = (WildcardPermission) p;
 //把当前标签转分割成一个set集合(如one:page1:view:add 会分割成[[one], [page1], [view], [add]])
    List<Set<String>> otherParts = wp.getParts();
    int i = 0;
 //循环匹配权限标签
    for (Set<String> otherPart : otherParts) {
        // If this permission has less parts than the other permission, everything after the number of parts contained
        // in this permission is automatically implied, so return true
  //当全部循环匹配完没有返回false,则返回true,这个getparts()方法是获取当前角色当前循环的权限标签([[one], [page1], [view]])
        if (getParts().size() - 1 < i) {
            return true;
        } else {
            Set<String> part = getParts().get(i);
   /*如果包含有‘*'而且不包含当前分割后的标签则返回false,
    *当用户可以查看页面,也就是说当前角色拥有one:page1:view标签
    *这里【!part.contains(WILDCARD_TOKEN)】返回true,第二个【part.containsAll(otherPart)】one会跟当前标签匹**配one,
    *也就是说这里全部循环完返回的都是false,所以最后都没true,于是在上面返回了一个true。
            if (!part.contains(WILDCARD_TOKEN) && !part.containsAll(otherPart)) {
                return false;
            }
            i++;
        }
    }

小结一下:通过分析,我们看到了shiro在定义权限标签时,要主意匹配问题,不要存在包含问题,类似aaa 和aaab ,会导致后面标签失效。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持猪先飞。

[!--infotagslink--]

相关文章

  • Spring AOP 对象内部方法间的嵌套调用方式

    这篇文章主要介绍了Spring AOP 对象内部方法间的嵌套调用方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-08-29
  • Spring Cloud 中@FeignClient注解中的contextId属性详解

    这篇文章主要介绍了Spring Cloud 中@FeignClient注解中的contextId属性详解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-09-25
  • Springboot如何实现Web系统License授权认证

    这篇文章主要介绍了Springboot如何实现Web系统License授权认证,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-05-28
  • 如何在Spring WebFlux的任何地方获取Request对象

    这篇文章主要介绍了如何在Spring WebFlux的任何地方获取Request对象,帮助大家更好的理解和使用springboot框架,感兴趣的朋友可以了解下...2021-01-26
  • 详解SpringCloudGateway内存泄漏问题

    这篇文章主要介绍了详解SpringCloudGateway内存泄漏问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-07-16
  • Spring为什么不推荐使用@Autowired注解详析

    @Autowired 注解的主要功能就是完成自动注入,使用也非常简单,但这篇文章主要给大家介绍了关于Spring为什么不推荐使用@Autowired注解的相关资料,需要的朋友可以参考下...2021-11-03
  • Springboot如何使用mybatis实现拦截SQL分页

    这篇文章主要介绍了Springboot使用mybatis实现拦截SQL分页,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-06-19
  • 浅谈关于如何检测iOS14本地网络权限的一些思路

    这篇文章主要介绍了浅谈关于如何检测iOS14本地网络权限的一些思路,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-12-08
  • phpMyadmin创建数据库和设置用户权限图解

    phpmyadmin是一款很不错的WEB对mysql数据库管理软件,如果你想创建数据库与设置用户权限我们必须操作用户的很高的权限,如ROOT权限,下面我来给大家介绍介绍。 先来配...2016-11-25
  • SpringMVC文件上传原理及实现过程解析

    这篇文章主要介绍了SpringMVC文件上传原理及实现过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-07-15
  • Spring Data JPA 关键字Exists的用法说明

    这篇文章主要介绍了Spring Data JPA 关键字Exists的用法说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-06-10
  • tomcat启动完成执行 某个方法 定时任务(Spring)操作

    这篇文章主要介绍了tomcat启动完成执行 某个方法 定时任务(Spring)操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-09-25
  • 使用Maven 搭建 Spring MVC 本地部署Tomcat的详细教程

    这篇文章主要介绍了使用Maven 搭建 Spring MVC 本地部署Tomcat,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-08-16
  • C#判断程序是否是管理员权限运行的方法代码示例

    这篇文章主要介绍了C#判断程序是否是管理员权限运行的方法代码示例,本文直接给出实现代码例子,需要的朋友可以参考下...2020-06-25
  • Java Spring Cloud 负载均衡详解

    这篇文章主要介绍了Spring Cloud负载均衡及远程调用实现详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2021-09-18
  • SpringMvc自动装箱及GET请求参数原理解析

    这篇文章主要介绍了SpringMvc自动装箱及GET请求参数原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-09-19
  • Springboot使用thymeleaf动态模板实现刷新

    这篇文章主要介绍了Springboot使用thymeleaf动态模板实现刷新,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-08-31
  • SpringMvc获取请求头请求体消息过程解析

    这篇文章主要介绍了SpringMvc获取请求头请求体消息过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-09-17
  • Idea打包springboot项目没有.original文件解决方案

    这篇文章主要介绍了Idea打包springboot项目没有.original文件解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-07-26
  • spring boot 使用utf8mb4的操作

    这篇文章主要介绍了spring boot 使用utf8mb4的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-02-20