前后端分离之shiro实现权限控制的一些问题

前言

现在做项目,大多都是前后端分离;权限控制都是在后台实现,前端使用ajax调用后台接口。

但是ajax对接口返回的重定向是没发处理的,会出现异常(具体错误是哪个一时想不起来了);当shiro发现失效后的session时通常会将该请求重定向到loginUrl,或者是用户访问的某个资源权限不足时(会重定向到unAuthorizedUrl),这时Ajax请求基本都是出错的。

解决方案

修改重定向302为正常返回200

shiro默认实现了一些filter,使用不同的filter处理对应的权限拦截:

/**
 * Enum representing all of the default Shiro Filter instances available to web applications.  Each filter instance is
 * typically accessible in configuration the {@link #name() name} of the enum constant.
 *
 * @since 1.0
 */
public enum DefaultFilter {

    anon(AnonymousFilter.class),
    authc(FormAuthenticationFilter.class),
    authcBasic(BasicHttpAuthenticationFilter.class),
    logout(LogoutFilter.class),
    noSessionCreation(NoSessionCreationFilter.class),
    perms(PermissionsAuthorizationFilter.class),
    port(PortFilter.class),
    rest(HttpMethodPermissionFilter.class),
    roles(RolesAuthorizationFilter.class),
    ssl(SslFilter.class),
    user(UserFilter.class);

当用户没有登录或session失效后,是使用FormAuthenticationFilter进行拦截处理的,我们可以重写里面的onAccessDenied方法,从而修改重定向。

public class ShiroFormAuthenticationFilter extends FormAuthenticationFilter {

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        if (isLoginRequest(request, response)) {
            if (isLoginSubmission(request, response)) {
                if (log.isTraceEnabled()) {
                    log.trace("Login submission detected.  Attempting to execute login.");
                }
                return executeLogin(request, response);
            } else {
                if (log.isTraceEnabled()) {
                    log.trace("Login page view.");
                }
                //allow them to see the login page ;)
                return true;
            }
        } else {
            HttpServletRequest req = (HttpServletRequest)request;
            HttpServletResponse resp = (HttpServletResponse) response;
            if(req.getMethod().equals(RequestMethod.OPTIONS.name())) {
                resp.setStatus(HttpStatus.OK.value());
                return true;
            }

            if (log.isTraceEnabled()) {
                log.trace("Attempting to access a path which requires authentication.  Forwarding to the " +
                        "Authentication url [" + getLoginUrl() + "]");
            }
			//前端Ajax请求时requestHeader里面带一些参数,用于判断是否是前端的请求
            String ajaxHeader = req.getHeader(ShiroConstant.USERID);
            if (ajaxHeader != null || req.getHeader("xx") != null) {
                //前端Ajax请求,则不会重定向
                resp.setHeader("Access-Control-Allow-Origin",  req.getHeader("Origin"));
                resp.setHeader("Access-Control-Allow-Credentials", "true");
                resp.setContentType("application/json; charset=utf-8");
                resp.setCharacterEncoding("UTF-8");
                PrintWriter out = resp.getWriter();
                JSONObject result = new JSONObject();
                result.put("message", "请重新登录!");
                result.put("statusCode", -401);
                out.println(result);
                out.flush();
                out.close();
            } else {
                saveRequestAndRedirectToLogin(request, response);
            }
            return false;
        }
    }
}

同理,用户权限不足是使用AuthorizationFilter进行拦截处理的,我们依然可以重写里面的onAccessDenied方法,如下:

public class ShiroRoleAuthorizationFilter extends AuthorizationFilter {
    
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
        HttpServletRequest req = (HttpServletRequest)request;
        HttpServletResponse resp = (HttpServletResponse) response;
        if(req.getMethod().equals(RequestMethod.OPTIONS.name())) {
            resp.setStatus(HttpStatus.OK.value());
            return true;
        }
		//前端Ajax请求时requestHeader里面带一些参数,用于判断是否是前端的请求
        String ajaxHeader = req.getHeader(ShiroConstant.USERID);
        if (ajaxHeader != null || req.getHeader("xx") != null) {
            //前端Ajax请求,则不会重定向
            resp.setHeader("Access-Control-Allow-Origin",  req.getHeader("Origin"));
            resp.setHeader("Access-Control-Allow-Credentials", "true");
            resp.setContentType("application/json; charset=utf-8");
            resp.setCharacterEncoding("UTF-8");
            PrintWriter out = resp.getWriter();
            JSONObject result = new JSONObject();
            result.put("message", "权限不足!");
            result.put("statusCode", -403);
            out.println(result);
            out.flush();
            out.close();
            return false;
        }
        return super.onAccessDenied(request, response);
    }
}

这样,前端Ajax请求被Shiro拦截后就不会出现返回302重定向的问题了。

你可能感兴趣的:(前端,Shiro)