欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

探索前端登录验证、页面跳转与 headers token 的实践与思考

最编程 2024-08-08 10:37:45
...

目录

  • 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 }
View Code
2)spring-mvc.xml 拦截器配置如下:拦截器中配置了对 /pages/admin/** 下的所有静态页面进行拦截,除了 /pages/admin/login.html 页面;
 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>
View Code

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 })
View Code

存在的问题:

问题就在于(4)中管理员登录后的跳转语句:window.location.href = "/idevtools/pages/admin/index.html",其没有携带headers: {'token': getAdminToken()},所以在跳转时会被后端拦截,然后又重定向到 login.html,就这样一直循环;
回到目录

3 我想到的解决方案

3.1 前端跳转时携带headers{'token': token} 不就行了(经验证不可行)

很遗憾,我找了好多资料,目前发现并不能实现这样的效果;一般用js做前端跳转,代码为:window.location.href = "/idevtools/pages/admin/index.html",查阅资料后得知 window.location 中并不支持 headers 的设置,如下:

(图源: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 }
View Code

需要注意的是:这里的url不是直接的静态页面形式,比如你要访问 /pages/admin/index.html ,这里的url就可写为:/idevtools/pages/admin/index;

2)后端设置相应的 AdminHtmlController.java 来处理这个请求;
  • 因为前端请求时携带了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 }
View Code
关于SpringMVC视图解析器的请求转发和重定向,可以参考:SpringMVC系列(九)自定义视图、重定向、转发

3)前端 admin.js 中替换页面跳转的方法,将:window.location.href = "/idevtools/pages/admin/index.html" 改为 redirect('/idevtools/pages/admin/index')

4)在测试时出现了:*Error,原因我之前的博客已经分析过了:SSM 返回静态页面HTML Controller 被递归调用引起的*Error