ReqAndRespAndZuul的一些自己的见解,和超时异常的解方案

1、先说说请求和响应对象再过滤器里转发和重定向的总结

总结:

  1. 如果过滤器里发生了重定向或者转发,那么,原客户端收到的响应一定是最后重定向或者转发后的响应,前面都不算

  2. 但是如果原过滤器,放行了原请求,则原请求会走完原来的流程,

  3. 转发,是先走转发的请求方法,转发不走过滤器,而是直接走方法,然后回到原来的方法,因为转发是拿 当前请求和响应对象过去的,所以响应对象被改变了,回到原来的方法之后,继续走完原请求的过滤器链

  4. 重定向是先走完原 最后走重定向的方法 如果一号过滤器发生了重定向,依旧执行后面的过滤器链,执行完原请求所有的过滤器链,才开始走重定向的过滤器链以及重定向的方法。

  5. 但最后响应的结果一定是转发后或者重定向后的响应的数据!

   //重定向的话,就是等原请求的过滤器链走完,响应对象回到客户端,再发请求,再在走新的请求的过滤器链。
//转发,根本就不会重新走过滤器,而是直接走它的方法 但是会走拦截器
//不管是重定向还是转发,都不会阻断后续方法的执行,都会执行,
   //不管是重定向还是转发,都可以把响应头带上,重定向是发送两次请求,响应头只会给原请求加,而重定向以后的响应头则没有;而转发是因为同一个请求和响应对象,所以请求头和响应头,都能再转发以后拿到!!!

2、Feign调用微服务的转发和响应是什么样?

证明: 再用Feign进行微服务调用的时候不会进行转发重定向。主要目的是为了拿到数据。JSON。 而不是为了请求转发和重定向!
还剩三个问题

  1. 网关转发 之后重定向,就是 谁请求,谁就重定向,再转发也就是转发。就当网关就是客户端,发送请求一样。本质是转发,但是是特殊的转发。地址栏不变!
    2. 字符串,到底是JSON还是Text/HTML 给了必要的响应头就是Json,否则永远都是text/html
    3. 网关走不走过滤器呢?通过网关转发的请求会走,转发的请求的过滤器

3.小实验

实验2:
    1.   request.getServletContext().getRequestDispatcher("/test").forward(request,response); 这个必须是根目录下开始写
    2.  request.getRequestDispatcher("test")  这个是可以写相对路径,也就是这一级目录下的可以写,就是可以不加斜杠

4.一次转发以后 第一次的请求,和第二次请求的**request.getRequestURI()**会变吗?

会变的,第一次请求和第二次请求,虽然是同一个请求,但是因为转发是在服务器内部转发,客户端是不知道,所以路径不变,但是服务器知道,所以请求的路径会变!

5.网关访问微服务超时了怎么办

zuul:
  host:
    connect-timeout-millis: 5000  # 连接超时时间,单位为毫秒
    socket-timeout-millis: 10000  # 响应超时时间,单位为毫秒

ribbon:
  ConnectTimeout: 5000  #zuul集成了ribbon,底层有ribbon来实现负载均衡
  ReadTimeout: 5000
  SocketTimeout: 5000  #socketTimeout 这个是一个容易被忽略的原因 这些默认是一秒超时

6.后端解决跨域问题,添加不了响应头怎么办?

@CrossOrigin(origins = "*",allowedHeaders = "*",exposedHeaders = {"Key"})

添加暴露响应头的key就可以把对应的响应头暴露出去

7.AOP和注解实现 角色权限添加

1.自定义两个注解
package com.qf.lawer.annotationConfig;

import com.qf.lawer.enumConst.Logic;

import java.lang.annotation.*;

@Documented
@Retention(RetentionPolicy.RUNTIME) // 指定注解保留到运行时
@Target(ElementType.METHOD) // 指定注解可以应用在方法上
public @interface AddPermitForMethod {

    String[] value();

    Logic logical() default Logic.AND;
}

package com.qf.lawer.annotationConfig;

import com.qf.lawer.enumConst.Logic;

import java.lang.annotation.*;

@Documented
@Retention(RetentionPolicy.RUNTIME) // 指定注解保留到运行时
@Target(ElementType.METHOD) // 指定注解可以应用在方法上
public @interface AddRoleForMethod {

    String[] value();

    Logic logical() default Logic.AND;
}

2.自定义枚举类
package com.qf.lawer.enumConst;

public enum Logic {
    AND, OR
}

3.自定义切面
PermitForAspect
package com.qf.lawer.aop;

import com.qf.lawer.annotationConfig.AddPermitForMethod;
import com.qf.lawer.annotationConfig.AddRoleForMethod;
import com.qf.lawer.enumConst.Logic;
import com.qf.lawer.exceptionConfig.RolesAuthException;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;

@Aspect
@Component
public class PermitForAspect {

    @Around("@annotation(com.qf.lawer.annotationConfig.AddPermitForMethod)")
    public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        ServletRequestAttributes request = getRequestAttributes();
        String token = request.getRequest().getHeader("token");
        System.out.println(token);

        //解析token拿到权限



//        System.out.println("Before method");
        AddPermitForMethod annotation = getAnnotation(joinPoint);
        // 访问注解的内容
        if (annotation != null) {
            String[] value = annotation.value();
            Logic logical = annotation.logical();
            switch (logical){
                case AND:
                    List<String> listAnd = Arrays.asList(value);
                    List<String> userRoles = Arrays.asList(permits());//角色权限
                    boolean b = userRoles.containsAll(listAnd);
                    if (!b){
                        throw new RolesAuthException("权限不够,不允许访问该方法");
                    }
                    System.out.println("权限符合");
                    break;
                case OR:
                    List<String> listOr = Arrays.asList(value);
                    List<String> userRoles2 = Arrays.asList(permits());//角色权限
                    for (String s : listOr) {
                        userRoles2.contains(s);
                        System.out.println("权限符合");
                        break;
                    }
                        throw new RolesAuthException("权限不够,不允许访问该方法");
            }

        }
        Object result = joinPoint.proceed();
//        System.out.println("after AOP");

        return result;

    }


    public String[] permits(){
        return new String[]{"insert","delete"};
    }

    private AddPermitForMethod getAnnotation(ProceedingJoinPoint joinPoint) {
        // 获取目标方法
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();

        // 获取注解
        return method.getAnnotation(AddPermitForMethod.class);
    }

    public  ServletRequestAttributes getRequestAttributes() {
        RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
        return (ServletRequestAttributes) attributes;
    }

}

RolesAspect
package com.qf.lawer.aop;

import com.qf.lawer.annotationConfig.AddRoleForMethod;
import com.qf.lawer.enumConst.Logic;
import com.qf.lawer.exceptionConfig.RolesAuthException;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;

@Aspect
@Component
public class RolesAspect {

    @Around("@annotation(com.qf.lawer.annotationConfig.AddRoleForMethod)")
    public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        ServletRequestAttributes request = getRequestAttributes();
        String token = request.getRequest().getHeader("token");
        System.out.println(token);

        //解析token获取角色


//        System.out.println("Before method");
        AddRoleForMethod annotation = getAnnotation(joinPoint);
        // 访问注解的内容
        if (annotation != null) {
            String[] value = annotation.value();
            Logic logical = annotation.logical();
            switch (logical){
                case AND:
                    List<String> listAnd = Arrays.asList(value);
                    List<String> userRoles = Arrays.asList(roles());//角色权限
                    boolean b = userRoles.containsAll(listAnd);
                    if (!b){
                        throw new RolesAuthException("角色不符合,不允许访问该方法");
                    }
                    System.out.println("角色符合");
                    break;
                case OR:
                    List<String> listOr = Arrays.asList(value);
                    List<String> userRoles2 = Arrays.asList(roles());//角色权限
                    for (String s : listOr) {
                        userRoles2.contains(s);
                        System.out.println("角色符合");
                        break;
                    }
                        throw new RolesAuthException("角色不符合,不允许访问该方法");
            }

        }
        Object result = joinPoint.proceed();
//        System.out.println("after AOP");

        return result;

    }


    public String[] roles(){
        return new String[]{"guest","admin"};
    }

    private AddRoleForMethod getAnnotation(ProceedingJoinPoint joinPoint) {
        // 获取目标方法
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();

        // 获取注解
        return method.getAnnotation(AddRoleForMethod.class);
    }

    public  ServletRequestAttributes getRequestAttributes() {
        RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
        return (ServletRequestAttributes) attributes;
    }

}

8.网关向前端返回数据,但是无法使用 全局异常处理器

1.是因为 网关的还没走到 微服务的dispatcherservlet 网关这边的全局异常处理器捕获不到微服务的 异常。

2.如果发生了异常,就用响应对象发送一条数据就可以了

例:token网关过滤器

package com.qf.lawer.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import com.qf.lawer.utils.Const;
import com.qf.lawer.utils.JWTUtils;
import com.qf.lawer.utils.ZuulResultVoUtils;
import com.qf.lawer.vo.ResultVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Enumeration;

@Component
@Slf4j
public class TokenFilter extends ZuulFilter {


    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return Integer.MIN_VALUE;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();
        //是 路径名  /lawer-firm/firm/list
        String requestURI = request.getRequestURI();
        System.out.println(requestURI);
        if (requestURI.contains("lawer-login")){
            //代表用户登录注册的请求,可以放行
            return null;
        }
        String token = request.getHeader("Token");
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            System.out.println(headerNames.nextElement());
        }
        if (token==null){
            //让请求不要在发送给后续的微服务了 token为空
            currentContext.setSendZuulResponse(false);
            ZuulResultVoUtils.sendErrorResultVo(Const.ERRORCODE,"Token为空,请登录以后再来");
        }else {
            //验证Token
            try {
                boolean b = JWTUtils.validateToken(token);
                if (!b){
                    //b==false
                    log.info("token error");
                    currentContext.setSendZuulResponse(false);
                    ZuulResultVoUtils.sendErrorResultVo(Const.ERRORCODE,"您的Token不正确");
                }
            } catch (Exception e) {
                currentContext.setSendZuulResponse(false);
                ZuulResultVoUtils.sendErrorResultVo(Const.ERRORCODE,"您输入的Token不合法");
            }
        }


        return null;
    }




}

发送工具类

package com.qf.lawer.utils;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.netflix.zuul.context.RequestContext;
import com.qf.lawer.vo.ResultVo;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class ZuulResultVoUtils {
    public static void sendErrorResultVo(Integer code,String message){
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletResponse response = currentContext.getResponse();
        response.setContentType("application/json;charset=utf-8");
        ResultVo resultVo = ResultVo.error(code, message);
        ObjectMapper mapper = new ObjectMapper();
        String s=null;
        try {
           s = mapper.writeValueAsString(resultVo);
            response.getWriter().println(s);
        } catch (Exception e) {
            e.printStackTrace();
          }


    }
}

9.跨域问题,无法拿到请求头怎么办

CORS(跨域资源共享):在服务器端设置响应头,允许特定的域或所有域的请求访问资源。可以通过在响应头中添加Access-Control-Allow-Origin字段来指定允许的域,例如Access-Control-Allow-Origin: http://example.com。你还可以设置其他相关的CORS响应头字段,如Access-Control-Allow-Methods和Access-Control-Allow-Headers来控制允许的请求方法和请求头。

10.在springboot中使用servlet

Servlet

package com.qf.HomeWork.servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/h1")
public class HelloServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=utf-8");
        String username = req.getParameter("username");
        System.out.println(username);
        resp.getWriter().println("Hello from Servlet:"+username);
    }
}

在主启动类上面加上注解

@ServletComponentScan//扫描启动类所同级目录下所有目录下标记了@WebServlet("/h1") 的类  servlet

@ServletComponentScan注解会将带有@WebServlet@WebFilter@WebListener`注解的类自动扫描并注册到Spring Boot的IOC容器中。

11.在springMVC中/*与/的完全理解

@RequestMapping("/test")
public String test(Model model){

    System.out.println("进入了test方法");
    //他会先去找动态的资源,找不到再去找静态资源,再找不到就404;动态优先级比静态高,
    // 所以 /* 和/的区别, 就是/*包括jsp  /不包括jsp,因为jsp不是静态资源   ,所以如果把他作为dispatcherServlet 包含的话
    //他会按照动态资源找,找完以后,不会去静态资源找,所以爆404,但是如果用/,不让jsp页面走dispatcherServlet
    //他会去servlet容器下去寻找 对应的jsp页面,原因是jsp本身就是一个servlet。
    
    //templeaf模板它是集成了html静态资源所以转发的是html里会被当成静态资源,所以可以走dispatcherServlet
    //在dispatcherServlet做了静态资源的判断,常见的 html,css,js后缀的都是静态资源,但是都会先走一遍动态匹配,如果没匹配上,是这些静态资源后缀的,才交给servlet容器去找静态资源。
    
   return "/a.html";
}

12.用户购物车,怎么判断是自己的购物车

在用户登录的时候,会把用户的登录信息放在本地的cookie中,是设置时间的,不会随着浏览器的关闭而关闭,访问购物车的时候,登录信息还在,会查用户id为几的 购物车下有哪些商品,价格多少。过一段时间,如果用户没有访问的话,就清空一次该用户的购物车!

因为cookie本身存储在本地,本身是安全的。所以它每次访问一次请求会把cookie带入到后端服务器,验证通过是登录后的状态,服务器会从jwt里取出用户id查询它的订单或者购物车信息。

13.域名与cookie

浏览器在不同域名下会将Cookie分别存储和管理。

Cookie是与域名相关联的,每个域名都有自己的Cookie存储空间。当浏览器接收到来自不同域名的响应时,会将每个域名下的Cookie分别保存起来,并在之后的请求中将相应域名下的Cookie信息发送给服务器。

这意味着,不同域名之间的Cookie是相互独立的,它们之间不能共享。例如,当你访问example.com和example.net这两个域名时,浏览器会分别存储和管理这两个域名下的Cookie,它们之间不会相互干扰。

这种机制可以实现跨域访问的安全性。由于不同域名下的Cookie是相互独立的,因此一个域名下的JavaScript代码不能直接访问另一个域名下的Cookie,这可以防止恶意代码窃取其他域名下的Cookie信息。

14.跨域问题

跨域,如果浏览器直接访问服务器,是因为访问域名与目标服务器域名同源,所以不会产生跨域问题,

但是如果客户端所在的域,与服务器所在的域不同源,然后客户端发送ajax请求,来访问服务器,这会产生跨域问题。

因为比如 baidu与csdn域名不一样,所以它们两个的cookie都是相互独立的,拿不到对方的cookie,但是如果baidu的服务器允许csdn跨域请求,csdn的客户端可以将请求发送到baidu服务器来获取,baidu服务器的信息。

你可能感兴趣的:(自我理解,java)