shiro异步请求返回JSON响应

Posted mozq

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了shiro异步请求返回JSON响应相关的知识,希望对你有一定的参考价值。

shiro异步请求返回JSON响应

需求1:当shiro请求资源,但是没有进行认证时,默认是进行重定向,现在需要返回JSON响应。注意异步请求,服务器重定向后,ajax拿到的是浏览器重定向后的到的页面源码。
解决2: 自定义FormAuthenticationFilter。覆盖onAccessDenied方法。返回JSON字符串。并将自定义的过滤器添加到ShiroFilterFactoryBean,键的名称为authc。
需求2:ShiroFilterFactoryBean用注解时,过滤的urls被写死在代码中,需要将urls的配置放到配置文件中。
解决2:
    方法1:ShiroFilterFactoryBean不使用注解方法,而是xml配置注入。@ImportResource("classpath:shiro/shiro-config.xml")
    方法2:自己通过shiro的Ini类加载ini配置文件。读取自定义的urls。

步骤

自定义 authc 对应过滤器 FormAuthenticationFilter。覆盖 onAccessDenied 方法返回JSON响应。

将自定义过滤器添加到 ShiroFilterFactoryBean。名称为 authc 。

ResultFormAuthenticationFilter

package com.mozq.shiro.shiro01.config;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;

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

@Slf4j
public class ResultFormAuthenticationFilter extends FormAuthenticationFilter {

    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 {
            if (log.isTraceEnabled()) {
                log.trace("Attempting to access a path which requires authentication.  Forwarding to the " +
                          "Authentication url [" + getLoginUrl() + "]");
            }
            if(isAjaxRequest(request)){
                saveRequest(request);
                writeResult(response);
                return false;
            }
            //saveRequestAndRedirectToLogin(request, response);
            return false;
        }
    }

    private boolean isAjaxRequest(ServletRequest request){
        return request instanceof  HttpServletRequest && "XMLHttpRequest".equalsIgnoreCase(((HttpServletRequest)request).getHeader("X-Requested-With"));
    }

    private void writeResult(ServletResponse servletResponse){
        if(servletResponse instanceof HttpServletResponse){
            HttpServletResponse response = (HttpServletResponse) servletResponse;
            HashMap<String, String> result = new HashMap<>();
            result.put("code", "401");
            result.put("message","未登录");
            try {
                response.setHeader("Content-Type", "application/json;charset=UTF-8");
                response.getWriter().write(JSONObject.toJSONString(result));
                response.getWriter().flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
package com.yuantiao.smartcardms.config.shiro;

import org.apache.shiro.realm.Realm;
import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.apache.shiro.mgt.SecurityManager;

import java.util.HashMap;
import java.util.Map;

/**
 * @description:
 * @author: changzhou.xie@yuantiaokj.com
 * @date: 2019/11/1 14:14
 */
@Configuration
public class ShiroConfig {

    //自定义 org.apache.shiro.realm.Realm
    @Bean
    public Realm realm(){
        //IniRealm iniRealm = new IniRealm("classpath:user.ini");
        SysUserRealm realm = new SysUserRealm();
        return realm;
    }

    //定义 org.apache.shiro.mgt.SecurityManager
    @Bean
    public SecurityManager securityManager(Realm realm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(realm);
        return securityManager;
    }

    //定义 org.apache.shiro.spring.web.ShiroFilterFactoryBean
/*    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //过滤的路径
        Map<String, String> map = new HashMap<>();
        map.put("/**", "authc");
        map.put("/user/login/pc", "anon");//放行登录相关接口
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);

        shiroFilterFactoryBean.setLoginUrl("/login.html");//登录页面
        return shiroFilterFactoryBean;
    }*/
}

使用配置文件来创建ShiroFilterFactoryBean

package com.yuantiao.smartcardms;

import org.apache.catalina.connector.Connector;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ImportResource;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

/**
 * SpringBoot项目启动类
 */
@SpringBootApplication
@EnableScheduling/* 开启定时任务 */
@MapperScan("com.yuantiao.smartcardms.dao.mapper")
@ImportResource("classpath:shiro/shiro-config.xml")
public class SmartcardMsApplication {

    public static void main(String[] args) {
        SpringApplication.run(SmartcardMsApplication.class, args);
    }

    /**
     * CORS跨域请求解决403
     * @return
     */
    private CorsConfiguration buildConfig() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOrigin("*");
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        corsConfiguration.setAllowCredentials(true);
        return corsConfiguration;
    }

    /**
     * 跨域请求COOKIES携带SessionId
     * @return
     */
    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", buildConfig());
        return new CorsFilter(source);
    }

    /**
     * 请求链接[]{}非法字符
     * @return
     */
    @Bean
    public TomcatServletWebServerFactory webServerFactory() {
        TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
        factory.addConnectorCustomizers((Connector connector) -> {
            connector.setProperty("relaxedPathChars", ""<>[\]^`{|}");
            connector.setProperty("relaxedQueryChars", ""<>[\]^`{|}");
        });
        return factory;
    }

}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="filterChainDefinitionMap" >
            <!-- 用props无法保证资源是按照顺序加载到filterChainDefinitionMap的,会有问题 -->
            <!--<props>-->
                <!--<prop key="/user/add">anon</prop>-->
                <!--<prop key="/user/delete">roles[laoban]</prop>-->
                <!--<prop key="/user/login/pc">anon</prop>-->
                <!--<prop key="/user/logout">anon</prop>-->
                <!--<prop key="/**">authc</prop>-->
            <!--</props>-->
            <map>
                <entry key="/user/login/pc" value="anon"/>
                <entry key="/**" value="authc"/>
            </map>
        </property>
        <property name="loginUrl" value="/login.html"/>
        <property name="securityManager" ref="securityManager"/>
        <property name="filters">
            <map>
                <entry key="authc">
                    <bean class="com.yuantiao.smartcardms.config.shiro.ResultFormAuthenticationFilter"/>
                </entry>
            </map>
        </property>
    </bean>

</beans>

错误

<property name="filterChainDefinitionMap" >
    <value>
        /card.html = anon
        /** = authc
    </value>
</property>
<!--
Caused by: java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'java.util.Map' for property 'filterChainDefinitionMap': no matching editors or conversion strategy found

看到网上很多filterChainDefinitionMap这种写,但是一配置直接报错。value指定的是字符串,而filterChainDefinitionMap是一个LinkedHashMap对象。完全无法这样配置。
-->

bugs

jquery-3.1.1.min.js:4 POST http://localhost:9000/smartcard/user/login/pc 415
请求头:
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Set-Cookie: JSESSIONID=080D2E469D78CD39C88AE54950B6640D; Path=/smartcard; HttpOnly
Set-Cookie: rememberMe=deleteMe; Path=/smartcard; Max-Age=0; Expires=Sat, 02-Nov-2019 02:32:54 GMT

以上是关于shiro异步请求返回JSON响应的主要内容,如果未能解决你的问题,请参考以下文章

Shiro Ajax请求没有权限返回JSON,没有登录返回JSON

跨域情况下的shiro怎么配置返回json

Shiro权限控制-区分Ajax请求

postman添加断言

学习小片段——springboot 错误处理

响应json数据之发送ajax的请求