总结:
如果过滤器里发生了重定向或者转发,那么,原客户端收到的响应一定是最后重定向或者转发后的响应,前面都不算
但是如果原过滤器,放行了原请求,则原请求会走完原来的流程,
转发,是先走转发的请求方法,转发不走过滤器,而是直接走方法,然后回到原来的方法,因为转发是拿 当前请求和响应对象过去的,所以响应对象被改变了,回到原来的方法之后,继续走完原请求的过滤器链
重定向是先走完原 最后走重定向的方法 如果一号过滤器发生了重定向,依旧执行后面的过滤器链,执行完原请求所有的过滤器链,才开始走重定向的过滤器链以及重定向的方法。
但最后响应的结果一定是转发后或者重定向后的响应的数据!
//重定向的话,就是等原请求的过滤器链走完,响应对象回到客户端,再发请求,再在走新的请求的过滤器链。
//转发,根本就不会重新走过滤器,而是直接走它的方法 但是会走拦截器
//不管是重定向还是转发,都不会阻断后续方法的执行,都会执行,
//不管是重定向还是转发,都可以把响应头带上,重定向是发送两次请求,响应头只会给原请求加,而重定向以后的响应头则没有;而转发是因为同一个请求和响应对象,所以请求头和响应头,都能再转发以后拿到!!!
证明: 再用Feign进行微服务调用的时候不会进行转发重定向。主要目的是为了拿到数据。JSON。 而不是为了请求转发和重定向!
还剩三个问题
实验2:
1. request.getServletContext().getRequestDispatcher("/test").forward(request,response); 这个必须是根目录下开始写
2. request.getRequestDispatcher("test") 这个是可以写相对路径,也就是这一级目录下的可以写,就是可以不加斜杠
会变的,第一次请求和第二次请求,虽然是同一个请求,但是因为转发是在服务器内部转发,客户端是不知道,所以路径不变,但是服务器知道,所以请求的路径会变!
zuul:
host:
connect-timeout-millis: 5000 # 连接超时时间,单位为毫秒
socket-timeout-millis: 10000 # 响应超时时间,单位为毫秒
ribbon:
ConnectTimeout: 5000 #zuul集成了ribbon,底层有ribbon来实现负载均衡
ReadTimeout: 5000
SocketTimeout: 5000 #socketTimeout 这个是一个容易被忽略的原因 这些默认是一秒超时
@CrossOrigin(origins = "*",allowedHeaders = "*",exposedHeaders = {"Key"})
添加暴露响应头的key就可以把对应的响应头暴露出去
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;
}
package com.qf.lawer.enumConst;
public enum Logic {
AND, OR
}
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;
}
}
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;
}
}
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();
}
}
}
CORS(跨域资源共享):在服务器端设置响应头,允许特定的域或所有域的请求访问资源。可以通过在响应头中添加Access-Control-Allow-Origin字段来指定允许的域,例如Access-Control-Allow-Origin: http://example.com。你还可以设置其他相关的CORS响应头字段,如Access-Control-Allow-Methods和Access-Control-Allow-Headers来控制允许的请求方法和请求头。
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容器中。
@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";
}
在用户登录的时候,会把用户的登录信息放在本地的cookie中,是设置时间的,不会随着浏览器的关闭而关闭,访问购物车的时候,登录信息还在,会查用户id为几的 购物车下有哪些商品,价格多少。过一段时间,如果用户没有访问的话,就清空一次该用户的购物车!
因为cookie本身存储在本地,本身是安全的。所以它每次访问一次请求会把cookie带入到后端服务器,验证通过是登录后的状态,服务器会从jwt里取出用户id查询它的订单或者购物车信息。
浏览器在不同域名下会将Cookie分别存储和管理。
Cookie是与域名相关联的,每个域名都有自己的Cookie存储空间。当浏览器接收到来自不同域名的响应时,会将每个域名下的Cookie分别保存起来,并在之后的请求中将相应域名下的Cookie信息发送给服务器。
这意味着,不同域名之间的Cookie是相互独立的,它们之间不能共享。例如,当你访问example.com和example.net这两个域名时,浏览器会分别存储和管理这两个域名下的Cookie,它们之间不会相互干扰。
这种机制可以实现跨域访问的安全性。由于不同域名下的Cookie是相互独立的,因此一个域名下的JavaScript代码不能直接访问另一个域名下的Cookie,这可以防止恶意代码窃取其他域名下的Cookie信息。
跨域,如果浏览器直接访问服务器,是因为访问域名与目标服务器域名同源,所以不会产生跨域问题,
但是如果客户端所在的域,与服务器所在的域不同源,然后客户端发送ajax请求,来访问服务器,这会产生跨域问题。
因为比如 baidu与csdn域名不一样,所以它们两个的cookie都是相互独立的,拿不到对方的cookie,但是如果baidu的服务器允许csdn跨域请求,csdn的客户端可以将请求发送到baidu服务器来获取,baidu服务器的信息。