本篇写一下在SpringBoot中拦截器功能的实现,再配合一个登陆拦截的demo。最后再讲讲什么是CORS。
在SpringBoot1.5之前,都需要重写WebMvcConfigurerAdapter来添加自定义拦截器(已经@Deprecated了)
在SpringBoot2.0后,推荐实现WebMvcConfigurer或者继承WebMvcConfigurationSupport来实现。
WebMvcConfigurer配置类其实是Spring内部的一种配置方式,采用JavaBean的形式代替传统的xml配置文件形式来进行对框架的个性化配置,可以自定义一些Handler、Interceptor、ViewResolver、MessageConverter等。
新建一个SpringBoot项目,我们在config包下创建自己的MvcConfig,我们来看看接口中有哪些方法可以实现
乍一看有这么多可以实现的方法,我们看看常用的有哪些
常用的方法
/* 拦截器配置 */ default void addInterceptors(InterceptorRegistry registry) { } /* 视图跳转控制器 */ default void addViewControllers(ViewControllerRegistry registry) { } /** *静态资源处理 **/ default void addResourceHandlers(ResourceHandlerRegistry registry) { } /* 默认静态资源处理器 */ default void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { } /** * 这里配置视图解析器 **/ default void configureViewResolvers(ViewResolverRegistry registry) { } /* 配置内容裁决的一些选项*/ default void configureContentNegotiation(ContentNegotiationConfigurer configurer) { } /** 解决跨域问题 **/ default void addViewControllers(ViewControllerRegistry registry) { }我们知道在SpringBoot项目中的静态资源都是放在resources下,可以写一个静态资源处理的配置方法
这样设置的话就表示静态资源路径(/static)下的访问不会被拦截
注意这里也自己先写好拦截器,才能注册生效
@Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginHandlerInterceptor()) .addPathPatterns("/**") .excludePathPatterns("/index.html","/","/user/login","/css/*","/js/**","/img/**"); }这里表示我们只需要输入 http://localhost:8080/即可访问到index.html
先看一下项目一览
先编写一个controller类
发送post请求,代替了RequestMapping(value="/login",method=“post”)
跳转页面这里用了 return “redirect:/dashboard”;会直接调用controller,
return "dashboard"只会访问目录下的文件
我们简单的写一个login页面
这里面用了thymeleaf模板,如果后台校验后密码不正确,th:text=" m s g " t h : i f = " {msg}" th:if=" msg"th:if="{not #strings.isEmpty(msg)}会存在值,则会生效。
action配置为th:action="@{/login}" method=“post”,告诉模板跳转的post请求是/login
很简单的前端页面,几乎没有美化,先看能不能用,等下再美化
再写一个登陆成功返回的页面
此时我们是没配置拦截器的,跑起来以后发现在地址栏直接输入http://localhost:8080/dashboard也能直接访问后台地址,显然是不允许的。所以我们需要配置拦截器,当用户名和密码不正确时,页面跳回登录页面,并显示无权限访问
在com.feng下新建component包,取名LoginHandlerInterceptor实现HandlerInterceptor接口,并实现三个接口,全部选上
可以看到里面有三个方法,
preHandle 在请求处理之前进行调用(Controller方法调用之前)
postHandle 请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)
afterCompletion 在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行 (主要是用于进行资源清理工作)
我们的前置方法先去从session里看userName有没有值(我们前面在controller中已经把userName存入session中),如果没有值就强行返回到登录页面,并把未登录警告封装到msg中传给前端
最后是配置自己的拦截器
可以看到除了首页,我们那儿也去不了,除非登录成功
是不是觉得界面太丑了,我们可以用Bootstrap来优化前端页面
我们把login页面替换为如下
login.html
<!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>Title</title> <link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"> <link href="assets/css/signin.css" rel="stylesheet"> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.0/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script> </head> <body class="text-center"> <form class="form-signin" th:action="@{/login}" method="post"> <img class="mb-4" src="assets/img/bootstrap-solid.svg" alt="" width="72" height="72"> <h1 class="h3 mb-3 font-weight-normal">Please sign in</h1> <p style="color:red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p> <label class="sr-only" >Username</label> <input type="text" name="username" class="form-control" placeholder="Username" required="" autofocus=""> <label class="sr-only" >Password</label> <input type="password" name="password" class="form-control" placeholder="Password" required=""> <div class="checkbox mb-3"> <label> <input type="checkbox" value="remember-me"/> Remember me </label> </div> <button class="btn btn-lg btn-primary btn-block" type="submit" >Sign in</button> <p class="mt-5 mb-3 text-muted">© 2019-2020</p> </form> </body> </html>还有一个signin.css
html, body { height: 100%; } body { display: -ms-flexbox; display: flex; -ms-flex-align: center; align-items: center; padding-top: 40px; padding-bottom: 40px; background-color: #f5f5f5; } .form-signin { width: 100%; max-width: 330px; padding: 15px; margin: auto; } .form-signin .checkbox { font-weight: 400; } .form-signin .form-control { position: relative; box-sizing: border-box; height: auto; padding: 10px; font-size: 16px; } .form-signin .form-control:focus { z-index: 2; } .form-signin input[type="email"] { margin-bottom: -1px; border-bottom-right-radius: 0; border-bottom-left-radius: 0; } .form-signin input[type="password"] { margin-bottom: 10px; border-top-left-radius: 0; border-top-right-radius: 0; }那个图标的话自己随便放个图就行
就完事儿了,下面讲讲跨域
首先说明,跨域问题主要存在浏览器端,在我们后台java是不存在的问题。跨域的请求也能发出去,服务器也能收到并返回正确结果,但是结果被浏览拦截了
之所以会跨域,是因为受到了同源策略的限制,同源策略要求源相同才能正常进行通信,即协议、域名、端口号都完全一致。浏览器出于安全的考虑,使用 XMLHttpRequest对象发起 HTTP请求时必须遵守同源策略,否则就是跨域的HTTP请求,默认情况下是被禁止的。换句话说,浏览器安全的基石是同源策略。
在SpringBoot中解决跨域我们主要是用Cors来解决
CORS是一个W3C标准,全称是”跨域资源共享”(Cross-origin resource sharing),允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。它通过服务器增加一个特殊的Header[Access-Control-Allow-Origin]来告诉客户端跨域的限制,如果浏览器支持CORS、并且判断Origin通过的话,就会允许XMLHttpRequest发起跨域请求。
我们创建两个SpringBoot项目
两个项目中都添加web和thymeleaf依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>在provider项目的配置类里端口配置为8080
server.port=8080而consumer的配置为8081
server.port=8081在provider项目下的resources/templates目录下创建index.html
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Cors跨域请求</title> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.0/jquery.min.js"></script> </head> <body> <input type="button" onclick="getRequest()" value="GET请求"> <input type="button" onclick="postRequest()" value="POST请求"> <script> function getRequest() { $.get('http://localhost:8081/index', function (res) { $('body').html(res); }) } function postRequest() { $.post('http://localhost:8081/index', function (res) { $('body').html(res); }) } </script> </body> </html>然后在consumer项目下创建一个controller层
新建
IndexController.java
package com.feng.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; /** * <h3>springboot-cors-consumer</h3> * <p></p> * * @author : Nicer_feng * @date : 2020-10-08 15:36 **/ @RestController public class IndexController { @GetMapping("index") public String gindex() { return "get hello;"; } @PostMapping("index") public String pindex() { return "post hello;"; } }两个项目都跑起来,我们点击get post请求后发现报错
可以看到报错为
Access to XMLHttpRequest at 'http://localhost:8081/index' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.翻译一下大概意思是说我们从8080端口访问8081收到了CORS的限制,没有开启跨域
我们只需要在consumer项目中的controller上对应的映射路径加上@CrossOrigin(value = “http://localhost:8080”),表示允许从8080端口访问即可,加入后重启consumer项目
可以看到成功访问到8081中的项目资源
还记得博客刚开始中的WebMvcConfigurer接口里有个眼熟的方法吗
没错呀,我们也可以通过项目配置,为整个项目开启跨域,咋整?也实现这个方法,新建一个CorsConfig类
CorsConfig.java
package com.feng.config; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** * <h3>springboot-cors-consumer</h3> * <p></p> * * @author : Nicer_feng * @date : 2020-10-08 16:03 **/ public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { //所有请求都会处理跨域 registry.addMapping("/**") // 允许谁访问 .allowedOrigins("http://localhost:8080") // 允许通过的请求数 .allowedMethods("*") // 允许的请求头 .allowedHeaders("*"); } }我们把刚才controller上配置的@CrossOrigin注释掉,重启服务器
收工