springboot shiro权限管理【三】:测试框架搭建之shiro授权
授权这块真的是头疼,实现之后返现如此之简单,网上查资料大部分都是认证这里,而授权相关的比较少,再加上springboot整合shiro实现前后台分离授权返回json是真的没看到,后来没办法,只能自己看源码。
列出来下我这里的一个实现过程:(备注:做的是前后端分离的,如果是不分离的就不用重写filter了)
1.有了之前做shiro认证的经验,我猜想到shiro授权,如果授权失败想要返回json格式数据,也是需要重写拦截器的,下面是shiro拦截器:
下面看下shiro PermissionsAuthorizationFiler.java源码实现
/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.shiro.web.filter.authz; import java.io.IOException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import org.apache.shiro.subject.Subject; /** * Filter that allows access if the current user has the permissions specified by the mapped value, or denies access * if the user does not have all of the permissions specified. * * @since 0.9 */ public class PermissionsAuthorizationFilter extends AuthorizationFilter { //TODO - complete JavaDoc public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException { Subject subject = getSubject(request, response); String[] perms = (String[]) mappedValue; boolean isPermitted = true; if (perms != null && perms.length > 0) { if (perms.length == 1) { if (!subject.isPermitted(perms[0])) { isPermitted = false; } } else { if (!subject.isPermittedAll(perms)) { isPermitted = false; } } } return isPermitted; } }
看完就明白了吧,里面有个isAccessAllowed方法,中间isPermistted这个属性等于false肯定是授权失败的,然后就可以在这里返回我们要返回给前端的json数据,我们重写它就行了,看代码:
package com.yg.lkyw.interceptor.shiro; import com.alibaba.fastjson.JSONObject; import com.yg.lkyw.result.ResultCode; import org.apache.shiro.subject.Subject; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.util.HashMap; import java.util.Map; import org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter; /** * @author XXX * @version 1.0 * @Description: shiro拦截器(用于处理用户没有权限访问返回信息) * @date 2018年05月08日 */ public class CustsomPermissionsAuthorizationFilter extends PermissionsAuthorizationFilter { @Override public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException { Subject subject = getSubject(request, response); String[] perms = (String[]) mappedValue; boolean isPermitted = true; if (perms != null && perms.length > 0) { response.setContentType("application/json"); response.setCharacterEncoding("UTF-8"); PrintWriter out = null; if (perms.length == 1) { if (!subject.isPermitted(perms[0])) { //定义未登录返回的json信息 Map<String, Object> map = new HashMap<>(); map.put("msg", ResultCode.FORBIDDEN.getMsg()); map.put("code", ResultCode.FORBIDDEN.getCode()); out = response.getWriter(); out.write(JSONObject.toJSONString(map)); out.flush(); } } else { if (!subject.isPermittedAll(perms)) { isPermitted = false; //定义未登录返回的json信息 Map<String, Object> map = new HashMap<>(); map.put("msg", ResultCode.FORBIDDEN.getMsg()); map.put("code", ResultCode.FORBIDDEN.getCode()); out = response.getWriter(); out.write(JSONObject.toJSONString(map)); out.flush(); } } } return isPermitted; } }
这样就ok了,接下来我们要把自己定义的filter加入到shiro中:
@Configuration public class ShiroConfiguration { /** * ShiroFilterFactoryBean 处理拦截资源文件问题。 * 注意:单独一个ShiroFilterFactoryBean配置是或报错的,以为在 * 初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager * Filter Chain定义说明 1、一个URL可以配置多个Filter,使用逗号分隔 2、当设置多个过滤器时,全部验证通过,才视为通过 3、部分过滤器可指定参数,如perms,roles * */ @Bean public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager){ System.out.println("ShiroConfiguration.shirFilter()"); ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();//获取filters filters.put("authc", new CustomFormAuthenticationFilter());//自定义返回未登录需要返回给前端的json数据 filters.put("perms", new CustsomPermissionsAuthorizationFilter()) ;//自定义返回授权失败所需的json提示数据 // 必须设置 SecurityManager shiroFilterFactoryBean.setSecurityManager(securityManager); //拦截器. Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>(); //注意过滤器配置顺序 不能颠倒 //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了,登出后跳转配置的loginUrl filterChainDefinitionMap.put("/logout", "logout"); // 配置不会被拦截的链接 顺序判断 filterChainDefinitionMap.put("/static/**", "anon"); filterChainDefinitionMap.put("/ajaxLogin", "anon"); filterChainDefinitionMap.put("/system/login", "anon"); filterChainDefinitionMap.put("/WeChatController/**", "anon"); //filterChainDefinitionMap.put("/system/user/**", "perms[admin]");//这个是我写死做测试用的,这里要求必须要是admin权限的才可以访问 filterChainDefinitionMap.put("/**", "authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } @Bean public SecurityManager securityManager(){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(new ShiroRealm()); return securityManager; } }
下面是我们的realms:
package com.yg.lkyw.interceptor.shiro; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; /** * @author 梁荣兵 * @version 1.0 * @Description: * @date 2018年05月08日 */ public class ShiroRealm extends AuthorizingRealm { /* * 登录信息和用户验证信息验证(non-Javadoc) * @see org.apache.shiro.realm.AuthenticatingRealm#doGetAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken) */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { String username = (String)token.getPrincipal(); //得到用户名 String password = new String((char[])token.getCredentials()); //得到密码 if(null != username && null != password){ return new SimpleAuthenticationInfo(username, password, getName()); }else{ return null; } } /* * 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用,负责在应用程序中决定用户的访问控制的方法(non-Javadoc) * @see org.apache.shiro.realm.AuthorizingRealm#doGetAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection) */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) { SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); authorizationInfo.addStringPermission("user");//这里也是写死的,写了一个user权限的,跟上面的admin区分开,为了测试不通过 return authorizationInfo; } }
shiro授权配置简单实例到这里就ok了
最后提示:用框架,不能单纯用,有空,有兴趣也需要去看看他的实现原理,有问题或建议欢迎留言