springboot+shiro+vue前后端分离,未登录shiro控制重定向引起的问题

  shiro集成jwt后会对前端传过来的token进行校验,如果token过期,按照以前的逻辑是后端进行了重定向,开发环境是没有问题的,但是部署在生产环境使用了nginx路由后,发生了请求不到后端未登录接口异常。

由于后端重定向,nginx路由后端的前缀"/datastatistics"没有带上导致了请求不到相应接口的问题,增加重写加上路由后解决了问题。

nginx配置:

仔细思考后觉得,前后端分离后本身由前端来做路由的主导,后端不应该插手怎么跳转的逻辑。虽然这个方案能解决后端跳转后引起的问题,但是我们本是只把后端服务看做是数据的提供接口,所以再做进一步改善。增加过滤器,在过滤器中检查token,如果过期则不再跳转,返回token过期的信息给前端,前端做相应跳转。

import com.alibaba.fastjson.JSONObject;
import com.yntravelsky.datafusion.util.Constant;
import com.yntravelsky.datafusion.util.ServiceResult;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;

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

/**
 * jwt过滤器
 *
 * @Author yangfeng
 * @Description preHandle->isAccessAllowed->isLoginAttempt->executeLogin
 * @Date 2019-09-18
 * @Time 12:36
 */
public class JWTFilter extends BasicHttpAuthenticationFilter {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    /**
     * 如果带有 token,则对 token 进行检查,否则直接通过
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws UnauthorizedException {
        //判断请求的请求头是否带上 "Token"
        if (isLoginAttempt(request, response)) {
            //如果存在,则进入 executeLogin 方法执行登入,检查 token 是否正确
            try {
                executeLogin(request, response);
                return true;
            } catch (Exception e) {
                //token 错误
                responseError(response, e.getMessage());
            }
        }
        //如果请求头不存在 Token,则可能是执行登陆操作或者是游客状态访问,无需检查 token,直接返回 true
        return true;
    }

    /**
     * 判断用户是否想要登入。
     * 检测 header 里面是否包含 Token
     */
    @Override
    protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
        HttpServletRequest req = (HttpServletRequest) request;
        String token = req.getHeader("Authorization");
        return token != null;
    }

    /**
     * 执行登陆操作
     */
    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String token = httpServletRequest.getHeader("Authorization");
        JWTToken jwtToken = new JWTToken(token);
        // 提交给realm进行登入,如果错误他会抛出异常并被捕获
        getSubject(request, response).login(jwtToken);
        // 如果没有抛出异常则代表登入成功,返回true
        return true;
    }

    /**
     * 对跨域提供支持
     */
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        httpServletResponse.setHeader("Access-control-Allow-Origin", "*");
        httpServletResponse.setHeader("Access-Control-Allow-Methods", "*");
        httpServletResponse.setHeader("Access-Control-Allow-Headers", "*");
        // 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态
        if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            httpServletResponse.setStatus(HttpStatus.OK.value());
            return false;
        }
        return super.preHandle(request, response);
    }

    /**
     * 请求异常则需要重新登录
     */
    private void responseError(ServletResponse response, String message) {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        ServiceResult<String> ret = new ServiceResult<String>();
        // public static int NO_AUTHENTICATION = 401;//没有认证
        ret.setCode(Constant.NO_AUTHENTICATION);
        ret.setMsg(message);
        ret.setSucceed(false);
        String jsonString = JSONObject.toJSONString(ret);

        OutputStream out = null;
        try {
            out = response.getOutputStream();
            out.write(jsonString.getBytes());
            out.flush();
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

接口判断token后返回包装信息。

前端增加对返回状态的判断即可。

         if (respone.data.code == 401) {
            localStorage.clear();
            router.replace({name: 'Login'});
          } else {
            Notification.error({
              title: '提示',
              message: respone.data.msg
            });
          }
          reject(respone.data);

如果返回code为401,前端跳转登录。

发布了70 篇原创文章 · 获赞 63 · 访问量 23万+

猜你喜欢

转载自blog.csdn.net/xiaoxiangzi520/article/details/101360076