登录拦截的实现有以下三种方式
一、同步的方式
1、登录时把信息存储在session中
@PostMapping("login") public String login(String name, String password, HttpSession session) { //判断用户名或密码是否为空 if (null == name || "" == name || null == password || "" == password) { session.setAttribute("name", name); session.setAttribute("name", password); session.setAttribute("msg", "用户名或密码为空"); System.out.println("用户名或密码为空"); return "redirect:/login.jsp"; } Employee employee = employeeService.login(name, password); if (Objects.isNull(employee)) { session.setAttribute("name", name); session.setAttribute("name", password); session.setAttribute("msg", "用户名或密码错误"); System.out.println("用户名或密码错误"); return "redirect:/login.jsp"; } session.setAttribute("employee", employee); System.out.println("success"); return "redirect:/list"; }2、定义拦截器,判断是否登录
登陆拦截器 public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, Object handler) throws Exception { Object employee = request.getSession().getAttribute("employee"); if (employee==null){ System.out.println("没有登陆"); request.getSession().setAttribute("msg","\uD83C\uDF36\uD83D\uDC14先登录"); response.sendRedirect("login.jsp"); return false; } return true; } }3、没有登录时重定向至登录页(同步的方式中重定向是成功的)
二、异步的方式——前端页面在项目中
代码同方式三
1、登录时把信息存储在session中
2、定义拦截器,判断是否登录
3、没有登录时,给前端返回统一的状态码(抛异常),让前端跳转(异步的方式,重定向是无效的)
三、异步的方式——前端页面不在项目中(前后端分离)
我们要知道的事:
在我们编写前端的时候,使用了vue,可能就没有了jQuery,没有了jQuery就证明没有了$.get、$.post、$.ajax,因此想要发送请求,就只能使用原生的ajax,但是使用原生的ajax代码太多不够优雅,所以此时我们需要依赖另一个ajax请求的封装库Axios。
注意:
这种方式默认是不携带cookie的,每一次的请求都会创建一个新的会话(新创建一个session),不携带cookie就无法根据cookie中session的ID确定要获取的session
前两种方式中session的获取默认是没有问题的,因为前两种默认携带cookie
所以第三种方式的实现是在第二种方式的基础上,让请求携带cookie
1、让请求携带cookie
第一步:后台(Java)允许携带
例如在使用跨域资源共享(CORS)方式解决跨域问题时,代码如下
配置类的编写方式 /** * 配置解决跨域问题 * 等级于配置文件中的<mvc:cors></mvc:cors> * * @param registry */ @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") .allowedHeaders("*") .allowedMethods("*") //是否允许携带cookie .allowCredentials(true); } 配置文件的编写方式
<!-- 解决API接口跨域问题配置 Spring MVC 版本必须是 4.2 及以上 --> <mvc:cors> <mvc:mapping path="/**" allowed-origins="*" allowed-methods="POST, GET, OPTIONS, DELETE, PUT" allowed-headers="Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With" allow-credentials="true" max-age="3600" /> </mvc:cors>第二步:前端设置携带
在自定义axios时可以设置携带cookie
let myaxios = axios.create({ baseURL:'http://localhost:8080/', //设置携带cookie withCredentials:true, timeout:5000 });2、登录时把信息存储在session中
@PostMapping("doLogin") public AxiosResult<Void> doLogin(@RequestBody Map<String,String>map, HttpSession session){ System.out.println(map); String phone = map.get("phone"); String code = map.get("code"); String s = stringRedisTemplate.opsForValue().get(phone); if (s.equals(code)) { Employee employee = employeeService.findByPhone(phone); //将登陆信息放入session session.setAttribute("user",employee); //清除 stringRedisTemplate.delete(phone); System.out.println(AxiosResult.success().getData()); return AxiosResult.success(); } throw new MyLoginException(AxiosStatus.CODE_CHECK_ERROR); }3、定义拦截器,判断是否登录
登陆拦截器 public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, Object handler) throws Exception { Object user = request.getSession().getAttribute("user"); if (user==null){ System.out.println("没有登陆"); throw new MyLoginException(AxiosStatus.NOT_LOGIN); } return true; } }4、没有登录时,给前端返回统一的状态码(抛异常),让前端跳转
注解所需要的jar自行import
状态码的枚举类 import lombok.AllArgsConstructor; import lombok.Getter; @Getter @AllArgsConstructor public enum AxiosStatus { OK(2000,"操作成功"), ERROR(5000,"操作失败"), //登录相关状态码 PHONE_NOT_FOUND(3000,"手机号错误"), USER_NOT_ACTIVE(3001,"用户未激活"), CODE_CHECK_ERROR(3002,"验证码错误"), NOT_LOGIN(4004,"登录过期,请重新登录"), ; private int status; private String message; } 自定义异常 import lombok.AllArgsConstructor; import lombok.Data; @Data @AllArgsConstructor public class MyLoginException extends RuntimeException{ private AxiosStatus axiosStatus; } 处理异常类 import com.shangma.cn.common.AxiosResult; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; @RestControllerAdvice public class MyHandler { @ExceptionHandler(MyLoginException.class) public AxiosResult<Void> myHandler(MyLoginException e){ System.out.println(e.getAxiosStatus()); return AxiosResult.error(e.getAxiosStatus()); } }
登录拦截后的跨域问题
当自己添加拦截器时,如果你的请求满足了拦截器,请求继续向下执行,没有问题,但是当你的请求不满足拦截器时,将会出现跨域问题,哪怕你解决了跨域问题都未必有效。
解决思想:
在自定义拦截器前,解决跨域问题
操作方式
1、自定义Filter(Filter比spring mvc中的拦截器更早地执行)
过滤的是servlet的请求,我们的拦截器是spring mvc的拦截器,想要进入spring mvc的拦截器,前提是要进入spring mvc,进入springmvc 是通过dispatcherServler,满足dispatcherServler中设置的请求才能进入,恰巧Filter可以对servlet进行拦截
import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; 第一种解决跨域问题的方式 全局解决 //进入springmvc之前,处理跨域问题 拦截所有请求 @WebFilter("/*") public class MyFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } /** * 在过滤器中解决跨域问题 * @param servletRequest * @param servletResponse * @param filterChain * @throws IOException * @throws ServletException */ @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("过滤器执行了"); HttpServletRequest req = (HttpServletRequest) servletRequest; //设置跨域请求 String origin = req.getHeader("Origin"); HttpServletResponse response = (HttpServletResponse) servletResponse; if(origin == null) { origin = req.getHeader("Referer"); } // 允许指定域访问跨域资源 response.setHeader("Access-Control-Allow-Origin", origin); // 允许客户端携带跨域cookie,此时origin值不能为“*”,只能为指定单一域名 response.setHeader("Access-Control-Allow-Credentials", "true"); response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Cookie,token"); response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS"); filterChain.doFilter(req, response); } @Override public void destroy() { } }
2、修改spring mvc中的跨域过滤器的顺序
第一步:向父容器中添加一个组件CorsFilter
配置类的写法 /** * 向父容器中添加Filter,解决跨域问题完美解决二(局部) * @return */ @Bean public CorsFilter corsFilter(){ UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource(); CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.addAllowedOrigin("*"); corsConfiguration.addAllowedHeader("*"); corsConfiguration.addAllowedMethod("*"); corsConfiguration.setAllowCredentials(true); corsConfigurationSource.registerCorsConfiguration("/**",corsConfiguration); CorsFilter corsFilter = new CorsFilter(corsConfigurationSource); return corsFilter; }第二步:挂载到拦截器上
在web.xml的配置类中使用DelegatingFilterProxy代理,指定定义的过滤器即可
/** * 挂载到拦截器上,解决跨域问题方式二 * @return */ @Override protected Filter[] getServletFilters() { DelegatingFilterProxy proxy = new DelegatingFilterProxy(); proxy.setTargetBeanName("corsFilter"); Filter[] filters = new Filter[]{proxy}; return filters; }