bootstrap登录请求跳转页面_bootstrap请求失效怎么跳转到登录页面 - CSDN
精华内容
参与话题
  • 目录 1 前言 2 我的实现方式与存在的问题 3 我想到的解决方案 ...3.1 前端跳转时携带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)在测试时出现了:StackOverflowError,原因我之前的博客已经分析过了:SSM 返回静态页面HTML Controller 被递归调用引起的StackOverflowError
     
    5)修改代码,将请求地址改为:redirect('/idevtools/pages/admin/aindex'),AdminHtmlController.java 中的 adminIndex的@GetMapping改为@GetMapping("/aindex"),继续测试,发现没有递归调用,但是前端也没有跳转到 index.html 页面;

    所以,得出结论:要在后端做静态页面的跳转,还是需要重定向;当然如果你用的是jsp,确实可以用请求转发来做页面跳转,因为jsp最终会编译成Servlet;

    此外,即使上述的操作都可行,方案3.2 也不是一个好的设计。因为:
    • 1)拦截 /pages/admin/** 下的所有静态页面,意味着管理员模块的前端在进行页面跳转时都需要写专门的请求,而不能直接写静态页面的跳转;
    • 2)进而在 AdminHtmlController.java 中需要对专门的请求进行响应,这样,前端 /pages/admin/** 下有多少个页面需要跳转,后端 AdminHtmlController 中就需要写多少个 @GetMapping("/xxx") 来响应;这是一种很糟糕的设计;

    如果某件事的解决方案很复杂,那就该反思是不是哪里出问题了,毕竟“简洁是智慧的灵魂,冗长是肤浅的藻饰

    3.3 放弃后端对/pages/admin/** 下静态页面的拦截,在前端做登录检测和跳转

    注:后端 JWTAdminInterceptor 依旧会对管理员的操作进行拦截,如果管理员未登录,则跳转到登录页面;差别是不对 /pages/admin/** 下的静态页面进行拦截,普通用户有可能在不登录的情况下访问到管理员模块的相关页面;

    1)取消 spring-mvc.xml 中关于 /pages/admin/** 的拦截;

     1 <!-- 拦截器配置 southday 2019.02.26 -->
     2 <mvc:interceptors>
     3     <!-- JWT 身份验证拦截器,针对管理员需要先进行登陆后才能操作的请求进行拦截 -->
     4     <mvc:interceptor>
     5         <mvc:mapping path="/a/**"/>
     6         <mvc:exclude-mapping path="/a/login"/>
     7         <mvc:exclude-mapping path="/a/adminInfo"/>
     8         <bean class="cn.idevtools.interceptor.JWTAdminInterceptor"/>
     9     </mvc:interceptor>
    10 </mvc:interceptors>
    View Code

    2)admin.js 中依旧用 window.location.href = "/idevtools/pages/admin/index.html" 来做页面跳转;

    3)另外创建 admin-index.js,在 index.html 中引用该js文件,实现每次加载首页时都会向后端请求管理员信息,如果未获取到,说明管理员未登录,则跳转到登录页面;

    admin-index.js 如下:

     1 $(function() {
     2     axios({
     3         method: 'get',
     4         url: cookurl('/idevtools/a/adminInfo'),
     5         headers: {'token': getAdminToken()}
     6     }).then(function(resp) {
     7         let ret = resp.data
     8         if (ret.code == 'SUCCESS') {
     9             saveAdmin(ret.data)
    10         } else {
    11             console.log(ret)
    12             window.location.href = "/idevtools/pages/admin/login.html"
    13         }
    14     }).catch(function(error) {
    15         console.log(error)
    16     })
    17 })
    View Code

    index.html 如下:

     1 <!DOCTYPE html>
     2 <html lang="zh-CN">
     3 <head>
     4     <meta charset="utf-8">
     5     <title>IDevTools</title>
     6 </head>
     7 <body>
     8 welcome to admin index
     9 
    10 <script src="../../js/jquery-3.3.1.min.js"></script>
    11 <script src="../../js/bootstrap.min.js"></script>
    12 <script src="../../js/axios.min.js"></script>
    13 <script src="../../js/vue.min.js"></script>
    14 <script src="../../js/toastr.min.js"></script>
    15 <!-- my js -->
    16 <script src="../../js/my/common.js"></script>
    17 <script src="../../js/my/admin/admin-index.js"></script>
    18 </body>
    19 </html>
    View Code

    4)经过测试,可以实现我想要的效果:

    现在主要来看看:普通用户在什么情况下可以访问到管理员模块的相关页面

    从方案3.3的实现中可以发现,进行登录检测和页面跳转控制是在前端 admin-index.js 中实现的,那么我们只要设置浏览器禁用js,就可以不执行这段js代码,直接访问到 index.html 页面;

    Firefox 中禁用 js 设置如下:

    一起来看看是不是如我们预测的一样:

    经过验证,发现普通用户确实在某些情况下可以直接访问到管理员页面。上面只演示了浏览器禁用js的方式来绕过检测,当然可能还存在其他方式;

    其实我觉得这种情况是不应该出现的,所以我才会想拦截 /pages/admin/** 下所有静态页面的访问,但我一时间没找到好的解决方法,就只想到了方案3.3这种不太完美的方法。不过好在:
    • 1)普通用户不会无缘无故去禁用js,所以大部分的担心其实不必要的;
    • 2)即使攻击者绕过了前端的检测进入了管理员的页面,后端 JWTAdminInterceptor 依旧会对管理员的相关操作进行拦截,如果发现没有登录,同样会跳转到登录页面;
    • 3)差别无非是攻击者可以获取到管理员模块的静态页面代码;

    所以在进行Web开发时,重要的操作在后端都要做验证,不能指望前端来做验证,前端的校验只是为了方便大多数用户。不能图一时方便,让攻击者有可乘之机。

    回到目录

    4 其他相关代码

    common.js 部分代码如下:

     1 /** 通用 js
     2  * @author southday
     3  * @date 2019.02.27
     4  * @version v0.1
     5  */
     6 
     7 /** url更改器 southday 2019.03.01
     8  * 1) 前端单独开发,测试时,url前面需要加http://localhost:8080
     9  * 2) 集成到java web项目中时,url前面不用加http://localhost:8080
    10  * 该方法是为了方便以上两种情况的相互转换,真正部署时,要取消该方法的调用
    11  */
    12 function cookurl(url) {
    13     // return url;
    14     return 'http://localhost:8080' + url;
    15 }
    16 
    17 /** 更换验证码 */
    18 function changeVerifyCode() {
    19     return cookurl('/idevtools/jcaptcha.jpg?r=' + (Math.random()))
    20 }
    21 
    22 /** code = VALID_ERROR,表单验证失败,提示消息
    23  * southday 2019.03.01
    24  */
    25 function showValidMsgs(validMsgs) {
    26     for (i = 0, len = validMsgs.length; i < len; i++)
    27         toastr.warning(validMsgs[i].errorMsg)
    28 }
    29 
    30 /**
    31  * 从localStorage中获取adminToken
    32  * southday 2019.05.17
    33  * @returns {string}
    34  */
    35 function getAdminToken() {
    36     return localStorage.getItem("adminToken")
    37 }
    38 
    39 /**
    40  * 将adminToken保存到localStorage中
    41  * @param token
    42  */
    43 function saveAdminToken(token) {
    44     localStorage.setItem("adminToken", token)
    45 }
    46 
    47 /**
    48  * 将admin保存到localStorage
    49  * southday 2019.05.17
    50  * @param admin
    51  */
    52 function saveAdmin(admin) {
    53     localStorage.setItem("admin", ($.isEmptyObject(admin) ? null : JSON.stringify(admin)))
    54 }
    55 
    56 /**
    57  * 从localStorage中取user
    58  * southday 2019.05.17
    59  * @returns {admin}
    60  */
    61 function getAdmin() {
    62     let a = localStorage.getItem("admin")
    63     return $.isEmptyObject(a) ? null : JSON.parse(a)
    64 }
    View Code

    转载请说明出处,have a good time! :D

    转载于:https://www.cnblogs.com/southday/p/10885235.html

    展开全文
  • SpringBoot的默认启动时,对资源拦截的控制台信息如下:2018-04-13 10:02:30.090 INFO 78908 --- [ restartedMain] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type...

       SpringBoot的默认启动时,对资源拦截的控制台信息如下:

    2018-04-13 10:02:30.090  INFO 78908 --- [  restartedMain] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
    2018-04-13 10:02:30.090  INFO 78908 --- [  restartedMain] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
    2018-04-13 10:02:30.157  INFO 78908 --- [  restartedMain] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]

    其中Spring Boot 默认将 /** 所有访问映射到以下目录:

    classpath:/static
    classpath:/public
    classpath:/resources
    classpath:/META-INF/resources

    其中默认配置的 /webjars/** 映射到 classpath:/META-INF/resources/webjars/ 

    那么当我们自己来实现对静态资源进行拦截控制的时候,就需要在控制类中进行@Configuration注解的配置,与此同时增加@EnableWebMvc,增加该注解之后,默认的WebMvcAutoConfiguration中配置就不会生效,就需要自己完成每一项的控制。当然如果你要更加细粒度以及控制的更精确,那肯定是要对WebMvcAutoConfiguration类了解的更加深入。

    控制类代码如下:

    @Configuration
    @EnableWebMvc //这个注解使得默认配置失效
    public class DefineAdapter extends WebMvcConfigurerAdapter implements ApplicationContextAware {
    	
    	private static final Log logger = LogFactory.getLog(DefineAdapter.class);
        
    	private ApplicationContext applicationContext;
    	
    	public DefineAdapter(){
    		super();
    	}
    
    	@Bean
            public ViewResolver viewResolver() { 
    	    logger.info("ViewResolver"); 
    	    InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); 
    	    viewResolver.setPrefix("/WEB-INF/views/"); 
    	    viewResolver.setSuffix(".jsp"); 
    	    return viewResolver; 
           } 
    	
    	@Override
    	public void setApplicationContext(ApplicationContext arg0) throws BeansException {
    		 System.out.println("setApplicationContext方法");
    	     this.applicationContext = applicationContext;
    	}  
    	//这里是实现用户拦截的具体操作,类LoginInterceptor是用户登录拦截器
    	@Override
            public void addInterceptors(InterceptorRegistry registry) {
                 System.out.println("addInterceptors方法"); 
                 /*
    	  	 *  多个拦截器组成一个拦截器链
    		 *  excludePathPatterns 用户排除拦截
    		 *  addPathPatterns 用于添加拦截规则 
    		 *  拦截规则:除了"/show/getLogin" 其他都拦截
    	    * */ 		
               registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**").excludePathPatterns("/show/getLogin");//多个排除拦截的话,只需要用逗号隔开即可
               //这边还可以加好几个拦截器组成拦截器链
              super.addInterceptors(registry);
           }
    	//可以方便的将一个请求映射成视图,无需书写控制器,addViewCOntroller("请求路径").setViewName("请求页面文件路径")
    	@Override
    	public void addViewControllers(ViewControllerRegistry registry) {
    		registry.addViewController("/show/getLogin").setViewName("login");
    	}
    	
    	//自定义资源拦截路径可以和springBoot默认的资源拦截一起使用,但是我们如果自己定义的路径与默认的拦截重复,那么我们该方法定义的就会覆盖默认配置
            @Override
            public void addResourceHandlers(ResourceHandlerRegistry registry) {
              //这里也可以采用ResourceUtils.CLASSPATH_URL_PREFIX 它是:classpath:
              registry.addResourceHandler("/images/**").addResourceLocations("classpath:/WEB-INF/resources/images/");
              registry.addResourceHandler("/js/**").addResourceLocations("classpath:./WEB-INF/resources/js/");
              registry.addResourceHandler("/css/**").addResourceLocations("classpath:/WEB-INF/resources/css/"); 
              super.addResourceHandlers(registry);  
            }
    }

    用户登录拦截器:LoginInterceptor

    @Service
    public class LoginInterceptor extends HandlerInterceptorAdapter{
    
        protected static final Log logger = LogFactory.getLog(LoginInterceptor.class);
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException{
        	logger.info("------preHandle------");
            //获取session
            HttpSession session = request.getSession(true);
            //判断用户Name是否存在,不存在就跳转到登录界面
            if(request.getParameter("userName") == null){
            	//System.out.println("这是路径地址:"+request.getRequestURI());
            	response.sendRedirect("/show/getLogin");
                return false;
            }else{
            	 session.setAttribute("userName", session.getAttribute("name"));
                 return true;
            }
        }
        
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                ModelAndView modelAndView) throws Exception {
            System.out.println(">>>LoginInterceptor>>>>>>>请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)");
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
                throws Exception {
            System.out.println(">>>LoginInterceptor>>>>>>>在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)");
        }
    }

    界面访问的控制器:PageController

    @Controller
    @RequestMapping(value = "/show")
    public class PageController {
    	
        @RequestMapping("/getEditJsp")
        public String editJsp(ModelMap map) {
            return "editUser";
        }
    	
        @RequestMapping(value = "/getLogin")
        public String loginJsp(ModelMap map) {
            return "login";
        }
    }

    当我输入localhost:端口号/show/getEditJsp时候会被拦截,进行判断Session中的内容是否为空,空就跳转到登录界面,此时这边还涉及到了重定向带来的js,css等静态资源无法显示的问题:

    配置文件中请如下配置:

      resources:
        #静态资源扫描
        static-locations: classpath:/WEB-INF/resources/ 

    当然这个要和原本DefineAdapter中的静态资源拦截一起配合使用,这是我的项目界面和静态资源存放目录:


    那么界面中静态资源的引用如下:

      <script src="../js/jquery/jquery-2.1.3.min.js"></script>
       <script src="../js/bootstrap/bootstrap.js"></script>
       <script src="../js/bootstrap/bootstrap.min.js"></script>
       <link rel="stylesheet" type="text/css" href="../css/bootstrap.min.css"/>

       <script type="text/javascript" src="../js/hello.js"></script>

        那好,用户拦截以及重定向静态资源都已解决,但是如果你看我上一章搭配BootStrap使用会发现,原本我使用bootStarp和jquery都是使用maven进行版本管理的,为何现在是手动将js,css文件放置在文件夹下。这个问题是这样的,原本我采用maven进行管理的时候,当用户被拦截重定向到登陆界面之后,我的静态资源一直无法正常显示,试了很多方式,但是好像都没有很好的解决方式,我也不知道怎么解决,如果哪位同志能很好的解决这个问题,欢迎在下面评论以及私聊。

    springBoot管理的bootStrap和jquery默认放在resources文件夹下的static的webjars文件夹中,在界面的引用如下:

    <script src="/webjars/jquery/jquery.min.js"></script>
    <script src="/webjars/bootstrap/js/bootstrap.min.js"></script>

    <link rel="stylesheet" href="/webjars/bootstrap/css/bootstrap.min.css"/>

    我在DefineAdapter中的静态资源拦截是如下配置:

    registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/static/webjars/"); 

    在application.yml中,我也尝试了资源的路径配置,也尝试过没去配置,反正都没有显示,试了两天了也没出效果,所以在此集思广益

    展开全文
  • jfinal与bootstrap登录跳转实战

    千次阅读 2015-09-18 11:08:02
    前言:终于来了一篇有质量的文章,我个人感觉非常不错,《jfinal与bootstrap之间的登录跳转实战》。具体内容包含有点击登录弹出模态框、点击登录确认按钮后的validate、jfinal的validate、jfinal的session管理、ajax...

    前言:终于来了一篇有质量的文章,我个人感觉非常不错,《jfinal与bootstrap之间的登录跳转实战》。具体内容包含有点击登录弹出模态框、点击登录确认按钮后的validate、jfinal的validate、jfinal的session管理、ajax请求与返回信息处理、页面间智能跳转。

    弹出模态框以及jquery validate可以参照jquery weebox总结弹出窗口不居中显示?jquery validate初上手系列文章。

    从jfinal的validate说起

    当然你可以参考jfinal提供的帮助文档,我当然也必须是参照了官方文档,当然对于这种开源技术每个人在使用的过程中肯定有千奇百态的问题,那么依据我的实战结果,我想再赘述一遍也是非常有必要的。

    1.指定config中的路由

    me.add("/login", MembersController.class, "/pages/login");

    2.编写conroller类

    public class MembersController extends BaseController {
        @Before(MembersValidator.class)
        @ActionKey("/login")
        public void login() {
    
            // 获取用户名
            String name = getPara("username");
            // 获取密码
            String password = getPara("password");
    
            Members mem = Members.me.getMemByNamePasswd(name, CipherUtils.generatePassword(password));
    
            if (mem != null) {
                // 保存session
                getSession().setAttribute("username", name);
    
                // 最后登录ip
                mem.set("lastip", getRequest().getRemoteHost());
                mem.set("lastvisit", DateUtils.getCurrentTime());
    
                mem.update();
    
                ajaxDoneSuccess("登录成功!");
            } else {
                ajaxDoneError("用户不存在!");
            }
    
            // 跳转到前台发起请求的路径
            renderJson();
    
        }
    }
    

    注意:

    1. 使用before绑定validate
    2. 使用actionkey绑定前端请求action名
    3. 使用getSession().setAttribute来操作session,同时前端稍后介绍如何使用
    4. 封装ajaxDone系列方法进行数据格式绑定,前端稍后介绍
    5. 使用renderJson方法对ajax请求返回结果数据进行json格式输出
    6. 接下来你需要看看我封装的baseController

    3.BaseController

    package com.hc.jf.controller;
    
    import com.jfinal.core.Controller;
    
    public class BaseController extends Controller {
    
        protected void ajaxDone(int statusCode, String message) {
            setAttr("statusCode", statusCode);
            setAttr("message", message);
    
            // 跳转路径
            String forwardUrl = getPara("forwardUrl");
            if (forwardUrl == null || forwardUrl.equals("")) {
                forwardUrl = getRequest().getRequestURL().toString();
            }
    
            setAttr("forwardUrl", forwardUrl);
            setAttr("callbackType", getPara("callbackType"));
        }
    
        protected void ajaxDoneSuccess(String message) {
            ajaxDone(200, message);
        }
    
        protected void ajaxDoneInfo(String message) {
            ajaxDone(201, message);
        }
    
        protected void ajaxDoneSuccess(String message, String forwarUrl) {
            ajaxDone(200, message);
        }
    
        protected void ajaxDoneError(String message) {
            ajaxDone(300, message);
        }
    
        protected void ajaxDoneError(String message, String forwarUrl) {
            ajaxDone(300, message);
        }
    }
    

    注意:

    1. 分别封装成功、错误、info级别的信息
    2. 增加了statusCode、message、forwardUrl、callbackType四个属性。
    3. 以上两个属性和前端也有对应,稍后介绍。

    4.MembersValidator

    package com.hc.jf.validator;
    
    import com.hc.jf.entity.Members;
    import com.jfinal.core.Controller;
    import com.jfinal.validate.Validator;
    
    public class MembersValidator extends Validator {
    
        @Override
        protected void validate(Controller controller) {
            validateRequiredString("username", "usernameMsg", "请输入用户名!");
            validateRequiredString("password", "passwordMsg", "请输入密码!");
        }
    
        @Override
        protected void handleError(Controller controller) {
            controller.keepModel(Members.class);
            controller.render("login.jsp");
        }
    
    }
    

    注意:

    1. 这个validate确实没什么屌用,因为前端已经使用jquery validate进行了check,然而请注意,如果你没有使用jquery的或者为了网络安全等等,有这个也不错。
    2. 好吧,我其实不想加入这个validate,但是我觉得可以我还没有想到用处,但是心有戚戚焉,觉得它还是有用的。

    jfinal的session管理

    其实说到这,已经不是jfinal的session了,其实要说的是前端。

    <c:choose>
    <c:when test="${sessionScope.username!=null}">
            <span>
                <a href="javascript:void(0);" id="mycenter" style="">
                    ${sessionScope.username}<s class="icon_arrow icon_arrow_down"></s>
                </a>
                <i class="line"></i>
                <a href="/logout" title="退出" id="user_login_out" style="padding: 0 6px;">退出</a>
            </span>
            </c:when>
        <c:otherwise>
            <span>
                <a title="登录" href="javascript:show_pop_login();" id="show_pop_login">登录</a>
            </span>
        </c:otherwise>
    </c:choose>

    注意:

    1. 这里使用了${sessionScope.username}获取session数据,这只是很简单的一个应用。
    2. 1中好像没什么可说的,但重要的是,你弹出登录框后,需要重新回到对应的跳转页面,然后显示登录的信息,好吧,我觉得没有解释清楚,那么上一张图吧!
      这里写图片描述

    这个图片为了商业机密,我只能截图到这里了,哈哈。
    就像很多互联网网站一样,你如果是游客的话,也可以打开很多页面进行相关的信息进行查看,比如你可以打开1.html、2.html,但是这两个页面都可以点击登录按钮弹出登录框。那么问题来了,

    你怎么保证从1.html打开登录的时候还跳转到1.html,从2.html打开登录成功后还跳转到2.html。

    好吧,先说到这,这里卖个关子,我们继续看下个章节。

    ajax请求与返回信息处理

    1.弹出登录窗口

    /**
     * 弹出登录框
     */
    function show_pop_login() {
        $.weeboxs.open(common.ctx + "/pages/login/login.jsp", {
            boxid : 'pop_user_login',
            contentType : 'ajax',
            showButton : false,
            showCancel : false,
            showOk : false,
            title : '会员登录',
            width : 700,
            type : 'wee'
        });
    }

    这是弹出登录框,至于weebox,你可以查看jquery weebox总结

    注意:

    这里可能是从1.html打开的,也可能是从2.html页面打开的登录框。

    2.然后我们再来看看登录的form表单

    <form class="pop_login_form" action="${ctx}/login?callbackType=closeCurrent" method="post" onsubmit="return validateCallback(this, dialogAjaxDone);">
        <div class="row ">
            <div class="row">
                <label class="col-md-4" style="margin-top: 10px;" for="name">用户登录</label>
            </div>
            <div class="form-group">
                <div class="row">
                    <div class="col-md-2 col-md-offset-2 tr th">
                        <label for="name">账户</label>
                    </div>
                    <div class="col-md-5">
                        <input type="text" style="" class="form-control required" id="username" name="username" placeholder="请输入会员编号"
                            autocomplete="off">
                    </div>
                </div>
            </div>
            <div class="form-group">
                <div class="row">
                    <div class="col-md-2 col-md-offset-2 tr th">
                        <label for="name">密码</label>
                    </div>
                    <div class="col-md-5">
                        <input type="password" class="form-control required" id="password" name="password" placeholder="请输入登陆密码">
                    </div>
                </div>
            </div>
            <div class="row">
                <div class="checkbox">
                    <label> <input type="checkbox"> 记住我(下次自动登陆)
                    </label>
                </div>
            </div>
            <div class="row">
                <button type="submit" style="margin-bottom: 10px;" class="btn btn-default">提交</button>
            </div>
        </div>
    </form>

    注意:

    1. 你需要关注action=”${ctx}/login?callbackType=closeCurrent”
    2. 再关注οnsubmit=”return validateCallback(this, dialogAjaxDone);”

    好吧,我们先来看看登录的界面吧。
    这里写图片描述

    很漂亮的登录框,漂亮的不像实力派!嘻嘻。

    然后关键的部分来了,请继续关注下节,我把发送ajax请求也放在下节的代码中,就省得重复。

    页面间智能跳转

    1.提交请求

    /**
     * 普通ajax表单提交
     * 
     * @param {Object}
     *            form
     * @param {Object}
     *            callback
     * @param {String}
     *            confirmMsg 提示确认信息
     */
    function validateCallback(form, callback, confirmMsg) {
        var $form = $(form);
    
        if (!$form.valid()) {
            return false;
        }
    
        var _submitFn = function() {
            var forwardUrl = window.location.href;
            var formUrl = $form.attr("action");
            if (formUrl.indexOf("?") != -1) {
                formUrl += "&forwardUrl=" + forwardUrl;
            } else {
                formUrl += "?forwardUrl=" + forwardUrl;
            }
    
            $.ajax({
                type : form.method || 'POST',
                url : formUrl,
                data : $form.serializeArray(),
                dataType : "json",
                cache : false,
                success : callback || YUNM.ajaxDone,
                error : YUNM.ajaxError
            });
        }
    
        if (confirmMsg) {
            alertMsg.confirm(confirmMsg, {
                okCall : _submitFn
            });
        } else {
            _submitFn();
        }
    
        return false;
    }

    好吧,看到这,你也许会说我剽窃了DWZ的灵感,OK,既然zhanghuihua同学开源了,有必要我们就好好的利用是吧。

    注意:

    1. 你看到了forwardUrl的相关代码,没错,这个关键的字段就是来传递发起请求的页面路径,比如说1.html,2.html。
    2. 然后,ajax执行成功后,也就是登陆成功后,我们要执行callback方法,也就是dialogAjaxDone方法,那么你需要继续看下去。

    2.回调函数

    /**
     * dialog上的表单提交回调函数 服务器转回forwardUrl,可以重新载入指定的页面.
     * statusCode=YUNM.statusCode.ok表示操作成功, 自动关闭当前dialog
     */
    function dialogAjaxDone(json) {
        YUNM.ajaxDone(json);
        if (json[YUNM.keys.statusCode] == YUNM.statusCode.ok || json[YUNM.keys.statusCode] == YUNM.statusCode.info) {
            if ("closeCurrent" == json.callbackType) {
                close_pop();
            }
            // 如果指定了后调转页面,进行调转
            if (json.forwardUrl) {
                location.href = json.forwardUrl;
            }
        }
    }
        ajaxDone : function(json) {
            if (json[YUNM.keys.statusCode] == YUNM.statusCode.error) {
                if (json[YUNM.keys.message])
                    $.showErr(json[YUNM.keys.message]);
                ;
            } else if (json[YUNM.keys.statusCode] == YUNM.statusCode.timeout) {
                alertMsg.error(json[YUNM.keys.message]);
            }
        },

    注意:

    1. 第二串代码就是出于错误消息,诸如“用户不存在的”,还有timeout。
    2. 第一串代码就是回调函数,其作用就是成功后关闭弹出框,然后再跳转到对应页面。

    这里写图片描述
    这里写图片描述

    结语:OK,这样一篇文章希望能够给热爱jfinal和bootstrap的同学带来灵感!

    展开全文
  • 为了调试方便,禁用模板引擎的默认缓存机制,即修改页面会实时生效 spring: messages: basename: i18n.login...1、登录页面跳转 (1)首先创建一个controller文件夹下的LoginController类 @Controller public class

    为了调试方便,禁用模板引擎的默认缓存机制,即修改页面会实时生效

    spring:
      messages:
        basename: i18n.login
    #  禁用模板引擎缓存,修改页面会自动更新
      thymeleaf:
        cache: false
    

    edit configuration下do nothing 改成 update resources
    在这里插入图片描述

    1、登录页面的跳转

    (1)首先创建一个controller文件夹下的LoginController类

    @Controller
    public class LoginController {
    
        @PostMapping("/user/login")
        public String login(@RequestParam("username") String username,
                            @RequestParam("password") String password,
                            Map<String, Object> map) {
            if (!StringUtils.isEmpty(username) && password.equals("123")) {
                return "dashboard";
            } else {
                map.put("msg", "用户名或密码错误");
                return "login";
            }
        }
    }
    

    (2)在login.html修改action和添加name属性

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
        <meta name="description" content="">
        <meta name="author" content="">
        <title>Signin Template for Bootstrap</title>
        <!-- Bootstrap core CSS -->
        <link href="asserts/css/bootstrap.min.css" th:href="@{/webjars/bootstrap/4.5.0/css/bootstrap.css}" rel="stylesheet">
        <!-- Custom styles for this template -->
        <link href="asserts/css/signin.css" th:href="@{/asserts/css/signin.css}" rel="stylesheet">
    </head>
    
    <body class="text-center">
    <form class="form-signin" action="dashboard.html" th:action="@{/user/login}" th:method="post">
        <img class="mb-4" th:src="@{/asserts/img/bootstrap-solid.svg}" src="asserts/img/bootstrap-solid.svg" alt=""
             width="72" height="72">
        <h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">Please sign in</h1>
        <label class="sr-only" th:text="#{login.username}">Username</label>
        <input type="text" name="username" class="form-control" placeholder="Username" th:placeholder="#{login.username}"
               required=""
               autofocus="">
        <label class="sr-only" th:text="#{login.password}">Password</label>
        <input type="password" name="password" class="form-control" placeholder="Password"
               th:placeholder="#{login.password}" required="">
        <div class="checkbox mb-3">
            <label>
                <input type="checkbox" value="remember-me"/> [[#{login.remember}]]
            </label>
        </div>
        <button class="btn btn-lg btn-primary btn-block" type="submit" th:text="#{login.btn}">Sign in</button>
        <p class="mt-5 mb-3 text-muted">© 2017-2018</p>
        <a class="btn btn-sm" th:href="@{/index.html(l='zh_CN')}">中文12</a>
        <a class="btn btn-sm" th:href="@{/index.html(l='en_US')}">English</a>
    </form>
    </body>
    </html>
    

    ok,浏览器输入用户名(随意)和密码(123)后
    在这里插入图片描述

    ???????css样式呢???

    F5刷新,还有这种提示 ???
    在这里插入图片描述

    为了刷新不显示重复提交表单的提示,我们可以通过重定向跳转解决这一问题

    2、重定向跳转

    (1)在MyMvcConfig配置类中再注册一个返回dashboard视图的视图控制

    //使用WebMvcConfigurer可以来扩展SpringMVC的功能
    //@EnableWebMvc   不要接管SpringMVC
    @Configuration
    public class MyMvcConfig implements WebMvcConfigurer {
        //所有的WebMvcConfigurer组件都会一起起作用
        @Bean //将组件注册在容器
        public WebMvcConfigurer webMvcConfigurer() {
            WebMvcConfigurer webMvcConfigurer = new WebMvcConfigurer() {
                @Override
                public void addViewControllers(ViewControllerRegistry registry) {
                    registry.addViewController("/").setViewName("login");
                    registry.addViewController("/index.html").setViewName("login");
                    registry.addViewController("/main.html").setViewName("dashboard");
                }
            };
            return webMvcConfigurer;
        }
    
        @Bean
        public LocaleResolver localeResolver() {
            return new MyLocaleResolver();
        }
    }
    

    即这一段

    registry.addViewController("/main.html").setViewName("dashboard");
    

    (2)回到LoginController,使用redirect重定向

    @Controller
    public class LoginController {
    
        @PostMapping("/user/login")
        public String login(@RequestParam("username") String username,
                            @RequestParam("password") String password,
                            Map<String, Object> map) {
            if (!StringUtils.isEmpty(username) && password.equals("123")) {
                return "redirect:/main.html";
            } else {
                map.put("msg", "用户名或密码错误");
                return "login";
            }
        }
    }
    

    ok,再登陆一次,哇草
    在这里插入图片描述

    样式也有了,是不是很神奇,我也觉得!!

    3、数据校验

    在登陆页面中,如果我们想对用户名和密码进行校验,怎么做呢??
    (1)加入一个p标签并写th:if判断

    <p style="color: red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>
    

    4、编写登录拦截器

    应该大家都懂为什么要配置登录拦截器,这里不想多说,怎么做呢?
    (1)首先我们得通过HttpSession记录正在登录的用户信息
    LoginController

    @Controller
    public class LoginController {
    
        @PostMapping("/user/login")
        public String login(@RequestParam("username") String username,
                            @RequestParam("password") String password,
                            Map<String, Object> map,
                            HttpSession session) {
    
            if (!StringUtils.isEmpty(username) && password.equals("123")) {
                session.setAttribute("loginUser", username);
                return "redirect:/main.html";
            } else {
                map.put("msg", "用户名或密码错误");
                return "login";
            }
        }
    }
    

    (2)在components文件夹下创建一个登录拦截器实现HandlerInterceptor接口

    public class LoginInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            Object user = request.getSession().getAttribute("loginUser");
            if (user == null) {
                //未登陆,返回登陆页面
                request.setAttribute("msg", "没有权限请先登陆");
                request.getRequestDispatcher("/index.html").forward(request, response);
                return false;
            } else {
                //已登陆,放行请求
                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 {
    
        }
    }
    

    (3)创建完拦截器后,我们还要在MyMvcConfig类中注册拦截器

               @Override
                public void addInterceptors(InterceptorRegistry registry) {
                    registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**")
                            .excludePathPatterns("/index.html", "/", "/user/login",
                                    "/asserts/**", "/webjars/**");
                }
    

    Ok,大功告成

    展开全文
  • Bootstrap页面跳转

    千次阅读 2019-06-02 23:23:55
    页面的相互跳转 首先是一个登录首页界面: (1)点击“登录”会跳转到个人页面,管理,查看相应信息(信息还没有加入,目前只是一个页面)。 点击登录跳转为如下界面: (2)点击“立即注册”会跳转到第二个注册...
  • bootstrap table带跳转页面

    千次阅读 2019-03-29 12:05:54
    第一引入文件,js主要是bootstrap.js,还有table的js,还有语言包的js,还有跳转js <script src="js/jquery-2.1.1.min.js"></script> <script src="js/bootstrap.min.js">...
  • Bootstrap-Table本身不带跳转页的功能,所幸在网上找到了bootstrap-table-pagejump这个插件,依靠这个插件可以轻松实现跳转功能,如下图所示: bootstrap-table-pagejump下载 链接:...
  • 在Django框架中一定要理解并熟练应用url协议,所有的数据和页面跳转基本上都是通过url协议来实现的,可以理解成类似前后端分离项目中的前端路由用来显示页面,后断路由用来显示数据传递等,这里的路由就是 URL , ...
  • 图中我点击删除按钮之后, 弹出一个模态框让我选择是回首页还是, 上一页, 下一页首页没什么好说的了, 就是直接跳转, 没什么问题上一页, 下一页却是跳转到上一篇 或者下一篇, 所有的”文章“都是用的是同一个html模板,...
  • 页面,即当前页面是一个bootstrap table,最后一列为查看历史列,点“历史查询”,就弹出一个模态框,模态框中有一个bootstrap table,展示历史数据。 问题描述: 模态框里面的bootstrap table不发送请求到后台...
  • bootstrap3以及bootstrap-table搭建的前端页面中,表单包含以button标签作为按钮。 <div class="row"> <div class="col=md-12"> <table id = "table"></table> <!-- 表格...
  • 使用bootstrapPaginator分页输入末页数不跳转,点击末页却跳转 ``` $("#page").bootstrapPaginator({ bootstrapMajorVersion:...不知道是为什么,想看源码也找不到,无奈请求大家帮忙说说什么原因,谢谢了
  • 加载查询页面 function load() { selectdept(); selectjob(); $('#exampleTable') .bootstrapTable( { method : 'get', // 服务器数据的请求方式 get or post ur...
  • bootstrap-table刷新页面回到首页

    千次阅读 2018-08-31 17:28:37
    使用了bootstrap-table插件。第一次用,遇到了很多 坑。 其中,rows和total两个参数(含义请百度),是必须有的,大家一定要注意,在看别人的博客的时候,很难这么注意细节。所以走了一定的弯路。在把分页做出来...
  • 在使用bootstrap treeview插件的时候,...方法如下:第一步:插件的基本环境bootstrap-treeview.min.css(1.2.0)bootstrap-treeview.min.js(1.2.0)jquery.js第二步:对treeview插件的基本认识首先,这个插件实现...
  • 前言: 今天周末,闲来无事又看了看拦截器,发现不只是截获http请求,同样还可以制作很多好玩的东西,今天来写一个loading案例,请求http或者跳转渲染页面时,会有一定的等待时间,可能一两秒,也可能是十几秒,在等待的过程中...
  • 首先准备好用到的js和css ...bootstrap table插件下载地址 官网:https://bootstrap-table.com/ Github:https://github.com/wenzhixin/bootstrap-table 主要css和js bootstrap-table.css、jquery....
  • 登录时增加: Session["UserName"] = model.UserName; Controller: protected override void OnActionExecuting(ActionExecutingContext filterContext) { base.OnActionExecuting(filte...
  • springMVC 几种页面跳转方式

    千次阅读 2018-07-13 09:55:33
    原文地址:点击打开链接前面已经了解了Controller的几种配置方式 今天主要写一下响应界面跳转的几种方式1.在注解的方式中1.1通过HttpServletResponse的API直接输出(不需要配置渲染器)controller类的主要代码 12@...
  • 针对ajax请求时拦截器不跳转问题

    千次阅读 2020-05-12 21:07:46
    在有一次开发中,原本用的是js控制页面跳转,控制器来接收并对此请求进行拦截。 之后js模块需要增加逻辑代码,特殊要求,所以改成了ajax进行异步请求。 最后发现用ajax请求的地址在处理后拦截器并不拦截此请求,也...
1 2 3 4 5 ... 20
收藏数 8,226
精华内容 3,290
关键字:

bootstrap登录请求跳转页面