探索前端登录验证、页面跳转与 headers token 的实践与思考
目录
- 1 前言
- 2 我的实现方式与存在的问题
-
3 我想到的解决方案
- 3.1 前端跳转时携带headers{'token': token} 不就行了(经验证不可行)
- 3.2 前端跳转封装请求,携带headers{'token': token},后端请求转发 (经验证不可行)
- 3.3 放弃后端对/pages/admin/** 下静态页面的拦截,在前端做登录检测和跳转
- 4 其他相关代码
1 前言
在做工程实践项目的管理员模块时,我想实现下面的效果:
- 1)在未登录状态下通过url访问 /pages/admin/** 下的静态页面,除了 login.html,其他都会被拦截,然后跳转到 login.html 页面;
- 2)在 login.html 页面登录后,会自动跳转到 /pages/admin/index.html 页面;
先给个效果图,对应的是:解决方案 3.3 放弃后端对/pages/admin/** 下静态页面的拦截,在前端做登录检测和跳转
对录屏软件感兴趣的请戳:Apowersoft 免费在线录屏
回到目录
2 我的实现方式与存在的问题
1)后端定义 JWTAdminInterceptor.java 来验证登录状态,如果未登录则重定向到 /pages/admin/login.html 页面。代码如下:
1 /** 2 * JWT验证拦截器(管理员),对于需要身份认证的请求,必须先经过该拦截器处理 3 * @author southday 4 * @date 2019/2/26 5 */ 6 public class JWTAdminInterceptor extends HandlerInterceptorAdapter { 7 private static final Logger logger = LogManager.getLogger(JWTAdminInterceptor.class); 8 9 @Autowired 10 private AdminService adminService; 11 12 @Override 13 public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception { 14 String jws = JWTer.getToken(); 15 JWTer jwter = new JWTer(jws); 16 boolean flag = false; 17 if (!jwter.isUsable()) { 18 logger.info("权限验证失败,异常:" + jwter.getException().getMessage() + " | [token = " + jws + "]"); 19 } else if (!CommonConst.USER_TYPE_ADMIN.equals(jwter.getUserType())) { 20 logger.info("权限验证失败,用户类型不匹配,[token = " + jws + "]"); 21 } else { 22 flag = adminService.isAdminExists(jwter.getUserName()); 23 } 24 if (!flag) { 25 resp.setStatus(401); 26 resp.sendRedirect("/idevtools/pages/admin/login.html"); 27 } 28 return flag; 29 } 30 }
1 <!-- 拦截器配置 southday 2019.02.26 --> 2 <mvc:interceptors> 3 <!-- JWT 身份验证拦截器,针对管理员需要先进行登陆后才能操作的请求进行拦截 --> 4 <mvc:interceptor> 5 <mvc:mapping path="/a/**"/> 6 <!-- 配置管理员模块静态页面的拦截 southday 2019.05.17 --> 7 <mvc:mapping path="/pages/admin/**"/> 8 <mvc:exclude-mapping path="/a/login"/> 9 <mvc:exclude-mapping path="/a/adminInfo"/> 10 <mvc:exclude-mapping path="/pages/admin/login.html"/> 11 <bean class="cn.idevtools.interceptor.JWTAdminInterceptor"/> 12 </mvc:interceptor> 13 </mvc:interceptors>
3)前端在未登录的情况下访问:http://localhost:8080/idevtools/pages/admin/index.html,就会被拦截,然后重定向到管理员登录页面;
4)管理员 login.html 中加载了 admin.js 来实现登录,登录后要跳转到 /pages/admin/index.html 页面;如下:
1 /** 2 * 管理员登陆模态框 /pages/admin/login.html 3 * southday 2019.05.17 4 */ 5 let vmAdminLogin = new Vue({ 6 el: "#admin-login", 7 data: { 8 adminName: '', 9 password: '', 10 jcaptcha: '', 11 jcaptchaURL: cookurl('/idevtools/jcaptcha.jpg') 12 }, 13 methods: { 14 login: function() { 15 axios({ 16 method: 'post', 17 url: cookurl('/idevtools/a/login'), 18 params: { 19 adminName: vmAdminLogin.adminName, 20 password: vmAdminLogin.password, 21 jcaptcha: vmAdminLogin.jcaptcha 22 } 23 }).then(function(resp) { 24 let ret = resp.data 25 if (ret.code == 'VALID_ERROR') { 26 showValidMsgs(ret.data) 27 } else if (ret.code == 'FAILURE') { 28 toastr.error(ret.msg) 29 } else { 30 saveAdmin(ret.data) 31 saveAdminToken(resp.headers.token) 32 window.location.href = "/idevtools/pages/admin/index.html" 33 } 34 vmAdminLogin.changeJCaptcha() 35 }).catch(function(error) { 36 console.log(error) 37 vmAdminLogin.changeJCaptcha() 38 }) 39 }, 40 changeJCaptcha: function() { 41 vmAdminLogin.jcaptchaURL = changeVerifyCode() 42 }, 43 logout: function() { 44 axios({ 45 method: 'post', 46 url: cookurl('/idevtools/a/logout'), 47 headers: {'token': getAdminToken()} 48 }).then(function(resp) { 49 let ret = resp.data 50 if (ret.code == "SUCCESS") { 51 saveAdmin(null) 52 saveAdminToken(null) 53 } else { 54 toastr.error(ret.msg) 55 } 56 }).catch(function(error) { 57 console.log(error) 58 }) 59 } 60 } 61 })
存在的问题:
3 我想到的解决方案
3.1 前端跳转时携带headers{'token': token} 不就行了(经验证不可行)
(图源:Location 对象:https://www.runoob.com/jsref/obj-location.html)
3.2 前端跳转封装请求,携带headers{'token': token},后端请求转发 (经验证不可行)
1)在前端封装一个方法用来提交请求,参数为要跳转的目标url,如下:
1 function redirect(url) { 2 axios({ 3 method: 'get', 4 url: cookurl(url), 5 headers: {'token': getAdminToken()} 6 }).then(function(resp) { 7 consolog.log('跳转到' + url) 8 }).catch(function(error) { 9 console.log(error) 10 }) 11 }
需要注意的是:这里的url不是直接的静态页面形式,比如你要访问 /pages/admin/index.html ,这里的url就可写为:/idevtools/pages/admin/index;
- 因为前端请求时携带了headers {'token': token},而后端在做请求转发时会共用前一次请求的request和response;
- 所以在拦截器中可以获取到 token,进而正确跳转;(经过验证:请求确实转发了,但前端页面没跳转,在我看来,要在后端做静态页面的跳转,还是需要重定向;当然如果你用的是jsp,确实可以用请求转发来做页面跳转,因为jsp最终会编译成Servlet)
1 @Controller 2 @RequestMapping("/pages/admin") 3 public class AdminHtmlController { 4 @GetMapping("/index") 5 public String adminIndex() throws Exception { 6 System.out.println("请求收到"); 7 return "forward:/pages/admin/index.html"; 8 } 9 }
3)前端 admin.js 中替换页面跳转的方法,将:window.location.href = "/idevtools/pages/admin/index.html" 改为 redirect('/idevtools/pages/admin/index')