今天給你們帶來的是頁面防重提交之令牌機制技術,若有不足之處,敬請指正。那麼首先咱們必須知道頁面防重是什麼?html
例如:咱們在瀏覽器輸入表單信息提交後,會將數據插入到數據庫中,此時瀏覽器會保存上一次請求的數據,若是咱們不作頁面防重,當咱們點擊頁面刷新按鈕時,就會再次將已經填好的表單提交,再次插入到數據庫中。java
瀏覽器中的防重提交示例 |
---|
![]() |
要解決這個問題,首先要理解token機制(令牌機制)web
如何理解token機制:通俗的講就比如古代將軍帶兵出征打仗,可是將軍若是在千里以外須要執行皇帝的命令,只能皇帝派親信帶着皇帝的信物虎符(虎符一分爲二)來傳達命令。若親信帶着的虎符與將軍的虎符匹配,則證實爲皇帝真實命令。咱們此次的令牌機制與此情景十分類似。spring
token示意圖 |
---|
![]() |
Token機制的實現:數據庫
做用:用於標識方法是否須要防重提交瀏覽器
package com.xkt.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 自定義一個Token註解,用於標識防重提交的方法 * @author lzx * */ @Target(value=ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface TokenForm { //用於標記須要防重提交的方法,建立Token的屬性 boolean create() default false; //用於標記防重提交方法的,刪除Token的屬性 boolean remove() default false; }
package com.xkt.interceptor; import java.util.UUID; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import com.xkt.annotation.TokenForm; /** * @author lzx * */ public class TokenInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 第一步:得到調用處理方法的註解 // 1.得到方法 HandlerMethod hm = (HandlerMethod) handler; // 2.得到方法調用的Token的註解 TokenForm tokenForm = hm.getMethodAnnotation(TokenForm.class); // 第二步:判斷是否有Token註解 if (tokenForm != null) { HttpSession session = request.getSession(); if (tokenForm.create() == true) { // 將Token惟一標識建立在服務器中,並在頁面中做用域中取得,以便於在提交表單時進行驗證 session.setAttribute("token", UUID.randomUUID().toString()); } if (tokenForm.remove() == true) { // 判斷表單的Token是否與服務端的Token相同 // 1.獲取表單的Token String formToken = request.getParameter("token"); // 2.獲取服務端的Token Object sessionToken = session.getAttribute("token"); // 表單傳遞過來的Token與服務端相同,容許操做,而且刪除session的Token if (formToken.equals(sessionToken)) { // 若是相同,則移除服務端的Token session.removeAttribute("token"); } else { // 若不一樣,跳轉到指定的路徑(由頁面傳遞token.invoke來指定) String invoke = request.getParameter("token.invoke"); response.sendRedirect(request.getContextPath() + invoke); return false; } } } return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } }
package com.xkt.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.InterceptorRegistration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; import org.springframework.web.servlet.view.InternalResourceViewResolver; import org.springframework.web.servlet.view.JstlView; import com.xkt.interceptor.LoginStatusInterceptor; import com.xkt.interceptor.PermissionInterceptor; import com.xkt.interceptor.TokenInterceptor; /** * @author lzx * */ @Configuration @EnableWebMvc // <mvc:annotation-driver/> public class MvcConfig extends WebMvcConfigurerAdapter { //<mvc:default-servlet-handler/> @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); } // 配置視圖解釋器 @Bean public InternalResourceViewResolver getInternalResourceViewResolver() { InternalResourceViewResolver resolver = new InternalResourceViewResolver(); // 支持JSTL的視圖 resolver.setViewClass(JstlView.class); // 配置視圖前綴 resolver.setPrefix("/WEB-INF/views/"); // 配置視圖後綴 resolver.setSuffix(".jsp"); return resolver; } @Bean public PermissionInterceptor getPermissionInterceptor() { PermissionInterceptor permission = new PermissionInterceptor(); return permission; } @Override public void addInterceptors(InterceptorRegistry registry) { // 1.登陸狀態過濾 LoginStatusInterceptor loginStatus = new LoginStatusInterceptor(); InterceptorRegistration loginStatusRegistration = registry.addInterceptor(loginStatus); // 攔截全部請求 loginStatusRegistration.addPathPatterns("/**"); // 排除登錄請求不攔截 loginStatusRegistration.excludePathPatterns("/admin/loginAdmin"); // 2.權限控制 PermissionInterceptor permission = this.getPermissionInterceptor(); InterceptorRegistration permissionRegistration = registry.addInterceptor(permission); // 攔截全部請求 permissionRegistration.addPathPatterns("/**"); // 排除登錄請求 permissionRegistration.excludePathPatterns("/admin/loginAdmin"); // 排除註銷請求 permissionRegistration.excludePathPatterns("/admin/undoAdmin"); // 3.令牌攔截器 TokenInterceptor token= new TokenInterceptor(); InterceptorRegistration tokenRegistration = registry.addInterceptor(token); tokenRegistration.addPathPatterns("/**"); } }
(兩個type="hidden"的input標籤)服務器
<form action="${pageContext.request.contextPath}/admin/addAdmin" method="post" class="form-horizontal" role="form"> <!-- 頁面令牌 --> <!-- token的值從做用域中取出,在頁面提交時用來比對 --> <!-- 當頁面token與服務端token不匹配時,token.invole指定跳轉路徑 --> <input type="hidden" name="token" value="${sessionScope.token}"/> <input type="hidden" name="token.invoke" value="/admin/toAdminAdd"/> <div class="form-group"> <label class="col-sm-3 control-label no-padding-right" for="form-field-1"> 用戶名 </label> <div class="col-sm-9"> <input name="admin_name" type="text" id="form-field-1" placeholder="用戶名" class="col-xs-10 col-sm-5" /> </div> </div> <div class="form-group"> <label class="col-sm-3 control-label no-padding-right" for="form-field-1"> 帳號名 </label> <div class="col-sm-9"> <input name="admin_account" type="text" id="form-field-1" placeholder="帳號名" class="col-xs-10 col-sm-5" /> </div> </div> <div class="form-group"> <label class="col-sm-3 control-label no-padding-right" for="form-field-1"> 密碼 </label> <div class="col-sm-9"> <input name="admin_pwd" type="password" id="form-field-1" placeholder="密碼" class="col-xs-10 col-sm-5" /> </div> </div> <div class="form-group"> <label class="col-sm-3 control-label no-padding-right" for="form-field-1"> 用戶狀態 </label> <div class="col-sm-9"> <select name="admin_status"> <c:forEach var="dictionary" items="${applicationScope.global_dictionarys}"> <c:if test="${dictionary.dictionary_type_code==10002}"> <option value="${dictionary.dictionary_value}">${dictionary.dictionary_name}</option> </c:if> </c:forEach> </select> </div> </div> <div class="form-group"> <label class="col-sm-3 control-label no-padding-right" for="form-field-1"> 角色 </label> <div class="col-sm-9"> <select name="role_id"> <c:forEach var="role" items="${applicationScope.global_roles}"> <option value="${role.role_id}">${role.role_name}</option> </c:forEach> </select> </div> </div> <div class="form-group"> <div class="col-sm-7 text-right"> <button type="submit" class="btn btn-primary">增長後臺用戶</button> </div> </div> </form>
/** * 增長管理員 請求路徑:${pageContext.request.contextPath }/admin/addAdmin * * @param admin * @param request * @return */ @RequestMapping("/addAdmin") @TokenForm(remove=true) public String addAdmin(@RequestParam Map<String, Object> admin, HttpServletRequest request) { try { //將密碼md5加密後再插入 admin.put("admin_pwd", Md5Utils.md5((String)admin.get("admin_pwd"))); LOGGER.debug("------增長管理員------" + admin); Map<String, Object> result = adminService.insert(admin); if (result != null) { request.setAttribute("admin_add_msg", "增長管理員成功"); } else { request.setAttribute("admin_add_msg", "增長管理員失敗"); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); request.setAttribute("admin_add_msg", "增長管理員失敗"); } return "/manager/adminAdd"; } /** * 跳轉到增長頁面 請求路徑:${pageContext.request.contextPath}/admin/toAdminAdd * * @return */ @RequestMapping("/toAdminAdd") @TokenForm(create=true) public String toAdminAdd() { return "/manager/adminAdd"; }
版權說明:歡迎以任何方式進行轉載,但請在轉載後註明出處!session