Shiro 认证、授权失败返回JSON数据

时间:2022-12-09 22:52:45 类型:JAVA
字号:    

在前后端分离的应用中,需要shiro授权失败时,返回json格式数据, 如下是在springboot应用中,前端ajax请求, shiro授权失败,返回json数据的应用实例

1,  在ShiroFilterFactoryBean类中增加如下两行代码:

    1.png

2, 当然同时需要定义两个角色和权限返回json类

    2.1  CustomRolesAuthorizationFilter  类

import com.alibaba.fastjson.JSONObject;
import com.zhuangzi.springboot1101.configurations.JsonResult;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.StringUtils;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;
import org.apache.shiro.web.util.WebUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class CustomRolesAuthorizationFilter extends AuthorizationFilter {
    @Override
    protected boolean isAccessAllowed(ServletRequest req, ServletResponse resp, Object o) throws Exception {
        Subject subject = getSubject(req, resp);
        String[] rolesArray = (String[]) o;

        if (rolesArray == null || rolesArray.length == 0) { //没有角色限制,有权限访问
            return true;
        }
        for (int i = 0; i < rolesArray.length; i++) {
            if (subject.hasRole(rolesArray[i])) { //若当前用户是rolesArray中的任何一个,则有权限访问
                return true;
            }
        }
        return false;
    }
    public static boolean isAjaxRequest(HttpServletRequest request) {
        String requestedWith = request.getHeader("x-requested-with");
        if (requestedWith != null && requestedWith.equalsIgnoreCase("XMLHttpRequest")) {
            return true;
        } else {
            return false;
        }
    }
    /***
     * 请求过滤的回调方法
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
        Subject subject = getSubject(request, response);
        if (subject.getPrincipal() == null) {
            //未登录
            if (isAjaxRequest((HttpServletRequest)request)) {
                response.setCharacterEncoding("UTF-8");
                response.setContentType("application/json");
                JsonResult jsonResult = new JsonResult();
                jsonResult.setCode("401");
                jsonResult.setMsg("登录认证失效,请重新登录!");
                response.getWriter().write(JSONObject.toJSONString(jsonResult));
            }else {
                saveRequestAndRedirectToLogin(request, response);
            }
        } else {
            //已经登陆,没有权限
            String unauthorizedUrl = getUnauthorizedUrl();
            if(isAjaxRequest((HttpServletRequest)request)) {
                response.setCharacterEncoding("UTF-8");
                response.setContentType("application/json");
                JsonResult jsonResult = new JsonResult();
                jsonResult.setCode("401");
                jsonResult.setMsg("您没有权限执行该操作");
                response.getWriter().write(JSONObject.toJSONString(jsonResult));
            }else {
                if (StringUtils.hasText(unauthorizedUrl)) {
                    WebUtils.issueRedirect(request, response, unauthorizedUrl);
                } else {
                    WebUtils.toHttp(response).sendError(HttpServletResponse.SC_UNAUTHORIZED);
                }
            }
        }
        return false;
    }
}

 2.2  CustomPermsAuthorizationFilter  类

import com.alibaba.fastjson.JSONObject;
import com.zhuangzi.springboot1101.configurations.JsonResult;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.StringUtils;
import org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter;
import org.apache.shiro.web.util.WebUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class CustomPermsAuthorizationFilter extends PermissionsAuthorizationFilter {
    @Override
    public boolean isAccessAllowed(ServletRequest req, ServletResponse resp, Object o) {
        Subject subject = getSubject(req, resp);
        String[] rolesArray = (String[]) o;

        if (rolesArray == null || rolesArray.length == 0) { //没有角色限制,有权限访问
            return true;
        }
        for (int i = 0; i < rolesArray.length; i++) {
            if (subject.hasRole(rolesArray[i])) { //若当前用户是rolesArray中的任何一个,则有权限访问
                return true;
            }
        }
        return false;
    }
    public static boolean isAjaxRequest(HttpServletRequest request) {
        String requestedWith = request.getHeader("x-requested-with");
        if (requestedWith != null && requestedWith.equalsIgnoreCase("XMLHttpRequest")) {
            return true;
        } else {
            return false;
        }
    }
    /***
     * 请求过滤的回调方法
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
        Subject subject = getSubject(request, response);
        if (subject.getPrincipal() == null) {
            //未登录
            if (isAjaxRequest((HttpServletRequest)request)) {
                response.setCharacterEncoding("UTF-8");
                response.setContentType("application/json");
                JsonResult jsonResult = new JsonResult();
                jsonResult.setCode("401");
                jsonResult.setMsg("登录认证失效,请重新登录!");
                response.getWriter().write(JSONObject.toJSONString(jsonResult));
            }else {
                saveRequestAndRedirectToLogin(request, response);
            }
        } else {
            //已经登陆,没有权限
            String unauthorizedUrl = getUnauthorizedUrl();
            if(isAjaxRequest((HttpServletRequest)request)) {
                response.setCharacterEncoding("UTF-8");
                response.setContentType("application/json");
                JsonResult jsonResult = new JsonResult();
                jsonResult.setCode("401");
                jsonResult.setMsg("您没有权限执行该操作");
                response.getWriter().write(JSONObject.toJSONString(jsonResult));
            }else {
                if (StringUtils.hasText(unauthorizedUrl)) {
                    WebUtils.issueRedirect(request, response, unauthorizedUrl);
                } else {
                    WebUtils.toHttp(response).sendError(HttpServletResponse.SC_UNAUTHORIZED);
                }
            }
        }
        return false;
    }
}

顺便附上ShiroConfig配置类 及MyRealm类

import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.mgt.SecurityManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {
    private static final Logger logger = LoggerFactory.getLogger(ShiroConfig.class);
    /**
     * 注入自定义的realm
     * @return MyRealm
     */
    @Bean
    // <bean id = 'myAuthRealm' class=>
    public MyRealm myAuthRealm() {
        MyRealm myRealm = new MyRealm();
        logger.info("====myRealm注册完成=====");
        return myRealm;
    }

    /**
     * 注入安全管理器
     * @return SecurityManager
     */
    @Bean
    public SecurityManager securityManager() {
// 将自定义realm加进来
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(myAuthRealm());
        logger.info("====securityManager注册完成====");
        return securityManager;
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
// 定义shiroFactoryBean
        ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();
// 设置自定义的securityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        Map<String, Filter> filters = new LinkedHashMap<>();
        filters.put("roles", new CustomRolesAuthorizationFilter());
        filters.put("perms",new CustomPermsAuthorizationFilter());
        shiroFilterFactoryBean.setFilters(filters);

// 设置默认登录的url,身份认证失败会访问该url
        shiroFilterFactoryBean.setLoginUrl("/login");
// 设置成功之后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/success");
// 设置未授权界面,权限认证失败会访问该url
        shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");
// LinkedHashMap是有序的,进行顺序拦截器配置
        Map<String,String> filterChainMap = new LinkedHashMap<>();
// 配置可以匿名访问的地址,可以根据实际情况自己添加,放行一些静态资源等,anon表示放行
        filterChainMap.put("/images/**", "authc");
        //在实际应用中, 这个身份拦截大致是:filterChainMap.put("/admin/**", "authc");
        //即所有/admin/开头的url都必须 登陆 成功才有基本的资格访问页面

        // “/user/student” 开头的需要角色认证,是“admin”才允许
        filterChainMap.put("/user/student/**", "roles[学生]");
        //测试,先用一个非学生角色登陆,访问, 应该是无权访问
        //     再使用一个学生角色访问, 应该是可以访问

        // “/user/teacher” 开头的需要权限认证,是“user:create”才允许
        filterChainMap.put("/user/teacher*/**", "perms[\"user:add\"]");
        // 不同的角色可以有相同的权限,只有此权限就可以访问, 不一定是必须是什么角色
        // user:* : 匹配 user : 任意值
        // user:add: 匹配 user:add
        filterChainMap.put("/user/**", "authc");


        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainMap);
        logger.info("====shiroFilterFactoryBean注册完成====");
        return shiroFilterFactoryBean;
    }


}
import com.zhuangzi.springboot1101.entity.Users;
import com.zhuangzi.springboot1101.service.UsersService;
import org.apache.shiro.SecurityUtils;
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;

import javax.annotation.Resource;

//Realm : 领域
//        本实中实现了 有什么权限(包含 角色及权限, 身份认证)
public class MyRealm extends AuthorizingRealm {
    @Resource
    private UsersService usersService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        // 获取用户名
        String username = (String) principalCollection.getPrimaryPrincipal();
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
                                //授权信息
        // 给该用户设置角色,角色信息存在t_role表中取
        authorizationInfo.setRoles(usersService.getRoles(username));
        System.out.println(usersService.getRoles(username));
        // 给该用户设置权限,权限信息存在t_permission表中取
        authorizationInfo.setStringPermissions(usersService.getPermissions(username));
        System.out.println("权限是:" + usersService.getPermissions(username));
        return authorizationInfo;

    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        // 根据token获取用户名,如果您不知道该该token怎么来的,先可以不管,下文会解释
        String username = (String) authenticationToken.getPrincipal();
// 根据用户名从数据库中查询该用户
        Users users = usersService.getByUsername(username);
        if(users != null) {
// 把当前用户存到session中
            SecurityUtils.getSubject().getSession().setAttribute("user", users);
// 传入用户名和密码进行身份认证,并返回认证信息
            AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(users.getUsername(), users.getUserpwd(), "myRealm");
            return authcInfo;
        } else {
            return null;
        }

    }
}


<