shiro php,shiro从入门到放弃-初学者笔记

背景:使用SpringBoot + shiro + vue 搭建一个权限控制的系统

问题: 遇到的一些可能感兴趣的问题

1、doGetAuthorizationInfo不会被调用

2、自定义过滤器导致访问资源认证出现混乱(过滤器失效)

3、使用shiro进行动态的权限配置

4、更改用户角色,如何让shiro知道并在下一次访问能做出调整

5、前后端分离如何做到让unauthorizedUrl和loginUrl能访问到正确的路径(交由vue前端控制)

6、定义一个过滤器进行授权校验

问题一、doGetAuthorizationInfo不会被调用

如果是采用注解或者配置文件写死那么不会这么纠结,如果是一个beginner并且没学会跑就要飞,那么肯定在doGetAuthorizationInfo不调用吃过很大的苦头,官网指出了有几种途径可以让doGetAuthorizationInfo执行,这里需要动态权限,所以采用编码的方式

bVbiv87?w=1276&h=233

角色检查

//get the current Subject

Subject currentUser = SecurityUtils.getSubject();

if (currentUser.hasRole("administrator")) {

//show a special button‏

} else {

//don’t show the button?)‏

}

权限检查

If (currentUser.isPermitted(printPermission)) {

//do one thing (show the print button?)‏

} else {

//don’t show the button?

}

上面两种hasRole/isPermitted都会触发doGetAuthorizationInfo调用,但是这种触发方式并不是很好,因为多角色有相同的资源路径,会多次调用doGetAuthorizationInfo影响性能

具体参考文章Shiro授权链接,shiro动态权限

自定义过滤器导致访问资源认证出现混乱(过滤器失效)

下面代码重写过滤器的时候需要特别注意要用new CustomRolesAuthorizationFilter() 这么创建过滤器而不是使用@Bean,具体原因很复杂,可以看看这篇文章为什么这样子 。

然后讲讲这篇文章没有讲到的内容,为什么会这样? springboot使用ApplicationFilterChain管理过滤器,shiro的11个默认过滤器和自定义shiro过滤器(自定义过滤器可以覆盖默认过滤器,因为默认过滤器先加入Map缓存)是放在一个名为shiroFilter的对象交由ApplicationFilterChain管理,如果给自定义过滤器加上@Bean,那么springboot会将这些自定义的过滤器放到ApplicationFilterChain上管理。

有什么问题? 简单说就是shiroFilter对象执行完里面的shiro过滤器以后(login/xx = anon验证通过了)会继续执行ApplicationFilterChain对象剩余的过滤器,而自定义过滤器(重写authc也就是FormAuthenticationFilter)刚好就是这些剩余的过滤器中的一个,这时会这个login/xx又会调用自定义过滤器再验证一遍,这导致授权不通过。

/**

ProxiedFilterChain类doFilter方法可以了解一下

作用执行完当前shiro过滤器后回到springboot的过滤器

*/

public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {

if (this.filters == null || this.filters.size() == this.index) {

//we've reached the end of the wrapped chain, so invoke the original one:

if (log.isTraceEnabled()) {

log.trace("Invoking original filter chain.");

}

this.orig.doFilter(request, response);

} else {

if (log.isTraceEnabled()) {

log.trace("Invoking wrapped filter at index [" + this.index + "]");

}

this.filters.get(this.index++).doFilter(request, response, this);

}

}

/**

其实不用重写ShiroFilterFactoryBean,下面一个bean了解一下

*/

@Bean

public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager, ShiroService shiroService) {

ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

// 必须设置 SecurityManager

shiroFilterFactoryBean.setSecurityManager(securityManager);

// 设置无权限时跳转的 url;

shiroFilterFactoryBean.setUnauthorizedUrl("/login/unAuthor.do");

// 重写roles拦截规则 path = roles['role,role2']

Map extraFilter = shiroFilterFactoryBean.getFilters();

extraFilter.put("roles", new CustomRolesAuthorizationFilter());

extraFilter.put("authc", new CustomFormAuthenticationFilter());

shiroFilterFactoryBean.setFilters(extraFilter);

// 加载拦截资源

shiroFilterFactoryBean.setFilterChainDefinitionMap(shiroService.loadFilterChainDefinitions());

return shiroFilterFactoryBean;

}

使用shiro进行动态的权限配置

更改用户角色,如何让shiro知道并在下一次访问能做出调整

写个service交由spring管理,提供加载清除shiro缓存的授权内容的功能

public void updatePermission(ShiroFilterFactoryBean shiroFilterFactoryBean) {

synchronized (this) {

AbstractShiroFilter shiroFilter;

try {

shiroFilter = (AbstractShiroFilter) shiroFilterFactoryBean.getObject();

} catch (Exception e) {

throw new RuntimeException("get ShiroFilter from shiroFilterFactoryBean error!");

}

PathMatchingFilterChainResolver filterChainResolver = (PathMatchingFilterChainResolver) shiroFilter.getFilterChainResolver();

DefaultFilterChainManager manager = (DefaultFilterChainManager) filterChainResolver.getFilterChainManager();

// 清空老的权限控制

manager.getFilterChains().clear();

shiroFilterFactoryBean.getFilterChainDefinitionMap().clear();

shiroFilterFactoryBean.setFilterChainDefinitionMap(loadFilterChainDefinitions());

// 重新构建生成

Map chains = shiroFilterFactoryBean.getFilterChainDefinitionMap();

for (Map.Entry entry : chains.entrySet()) {

String url = entry.getKey();

String chainDefinition = entry.getValue().trim()

.replace(" ", "");

manager.createChain(url, chainDefinition);

}

}

}

前后端分离如何做到让unauthorizedUrl和loginUrl能访问到正确的路径(交由vue前端控制)

最简单做法就是指定一个能访问后台的路径,比如shiroFilterFactoryBean.setUnauthorizedUrl("/login/unAuthor.do");,然后写一个controller方法返回状态码,交给前端路由axios的拦截器进行拦截处理跳转。当然这里对登录处理可以重写FormAuthenticationFilter,在这个类中进行状态码的返回

定义一个过滤器进行授权校验

public class CustomRolesAuthorizationFilter extends AuthorizationFilter {

private HttpServletRequest request;

@Override

protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {

Subject subject = getSubject(request, response);

CmsUserEntity user = (CmsUserEntity) subject.getPrincipals().getPrimaryPrincipal();

if (null == user) {

return true;

}

String[] rolesArray = (String[]) mappedValue;

if (rolesArray == null || rolesArray.length == 0) {

return true;

}

List rolesList = Arrays.asList(rolesArray);

UserBiz userBiz = SpringUtil.getBean(UserBiz.class);

List roleNames = userBiz.findRoleNameByUserId(user.getId());

Set roleSet = new HashSet<>(roleNames);

boolean disjoint = Collections.disjoint(roleSet, rolesList);

return !disjoint;

}

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值