1、先说说请求和响应对象再过滤器里转发和重定向的总结
总结:
-
如果过滤器里发生了重定向或者转发,那么,原客户端收到的响应一定是最后重定向或者转发后的响应,前面都不算
-
但是如果原过滤器,放行了原请求,则原请求会走完原来的流程,
-
转发,是先走转发的请求方法,转发不走过滤器,而是直接走方法,然后回到原来的方法,因为转发是拿 当前请求和响应对象过去的,所以响应对象被改变了,回到原来的方法之后,继续走完原请求的过滤器链
-
重定向是先走完原 最后走重定向的方法 如果一号过滤器发生了重定向,依旧执行后面的过滤器链,执行完原请求所有的过滤器链,才开始走重定向的过滤器链以及重定向的方法。
-
但最后响应的结果一定是转发后或者重定向后的响应的数据!
//重定向的话,就是等原请求的过滤器链走完,响应对象回到客户端,再发请求,再在走新的请求的过滤器链。
//转发,根本就不会重新走过滤器,而是直接走它的方法 但是会走拦截器
//不管是重定向还是转发,都不会阻断后续方法的执行,都会执行,//不管是重定向还是转发,都可以把响应头带上,重定向是发送两次请求,响应头只会给原请求加,而重定向以后的响应头则没有;而转发是因为同一个请求和响应对象,所以请求头和响应头,都能再转发以后拿到!!!
2、Feign调用微服务的转发和响应是什么样?
证明: 再用Feign进行微服务调用的时候不会进行转发重定向。主要目的是为了拿到数据。JSON。 而不是为了请求转发和重定向!
还剩三个问题
- 网关转发 之后重定向,就是 谁请求,谁就重定向,再转发也就是转发。就当网关就是客户端,发送请求一样。本质是转发,但是是特殊的转发。地址栏不变!
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: 5000SocketTimeout: 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 {@Overridepublic String filterType() {return FilterConstants.PRE_TYPE;}@Overridepublic int filterOrder() {return Integer.MIN_VALUE;}@Overridepublic boolean shouldFilter() {return true;}@Overridepublic Object run() throws ZuulException {RequestContext currentContext = RequestContext.getCurrentContext();HttpServletRequest request = currentContext.getRequest();//是 路径名 /lawer-firm/firm/listString 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 {//验证Tokentry {boolean b = JWTUtils.validateToken(token);if (!b){//b==falselog.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 {@Overrideprotected 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服务器的信息。