欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-03-21 08:37:46
目录realm类shiro 配置类servicespring-shiro权限控制realm用户与角色实体role.java@data@entitypublic class role { @id...

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 ,会导致后面标签失效。

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