前後端分離實現登陸攔截
登入攔截的實現有以下三種方式
一、同步的方式
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; }