spring跨域处理

:::warning
注意:使用springsecurity时会出现跨域问题!在websecurityconfig上面加上.cors()方法!!!!!!
:::

https://blog.csdn.net/weixin_45059597/article/details/107490252

使用过滤器

过滤器其实不是spring管理的,而是servelet管理的,常用的GenericFilterBean,OncePerRequestFilter,spring管理的Interceptor

第一种

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.site.blog.filters;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

@Configuration
public class CorsConfig {
private CorsConfiguration buildConfig() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOriginPattern("*" );
//corsConfiguration.addAllowedOrigin("http://192.168.72.132");
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
corsConfiguration.setAllowCredentials(true);
return corsConfiguration;
}

@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
// 配置所有请求
source.registerCorsConfiguration("/**", buildConfig());
return new CorsFilter(source);
}
}

第二种写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@WebFilter(value = "/*")
@Component
public class CorsFilter extends GenericFilterBean {

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;

httpServletResponse.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, httpServletRequest.getHeader(HttpHeaders.ORIGIN));
httpServletResponse.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "Origin, X-Requested-With, Content-Type, Accept");
httpServletResponse.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "GET, POST, PUT, DELETE, OPTIONS");
httpServletResponse.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
httpServletResponse.setHeader(HttpHeaders.ACCESS_CONTROL_MAX_AGE, "3600");

if (!CorsUtils.isPreFlightRequest(httpServletRequest)) {
chain.doFilter(httpServletRequest, httpServletResponse);
}
}

}

或者(自己手动写请求头,推荐上面那种)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import org.springframework.context.annotation.Configuration;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebFilter(filterName = "CorsFilter ")
@Configuration
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin","*");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, PATCH, DELETE, PUT");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
chain.doFilter(req, res);
}
}

也可以使用拦截器:代码如下

:::warning
这里不能使用allowedOriginsPattern(“*”)配置多个
但是可以使用response.setHeader("Access-Control-Allow-Origin", request.getHeader("origin") );设置动态请求头实现跨域
:::

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
* @Author: Yangzhengqian
* @Description:
* @Date:Created time 2020/8/7 17:07
* @Modified By:
*/
@Component
public class InterceptorCORS implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "*");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept");
//浏览器会先发送一个试探请求OPTIONS,然后才会发送真正的请求,为了避免拦截器拦截两次请求,所以不能让OPTIONS请求通过
if (request.getMethod().equals(HttpMethod.OPTIONS.name())){
response.setStatus(HttpServletResponse.SC_OK);
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 {

}
}

然后添加webmvcconfig:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

@Configuration
public class WebMvcConf implements WebMvcConfigurer {

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
String filePath =File.separator + "HellohaoData" + File.separator;
//和页面有关的静态目录都放在项目的static目录下
//registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
//上传的图片在D盘下的OTA目录下,访问路径如:http://localhost:8081/OTA/d3cf0281-bb7f-40e0-ab77-406db95ccf2c.jpg
//其中OTA表示访问的前缀。"file:D:/OTA/"是文件真实的存储路径
//registry.addResourceHandler("/test/**").addResourceLocations("file:C:/test/");

//registry.addResourceHandler("/static/**")
// .addResourceLocations("classpath:/static/");
registry.addResourceHandler("/links/**").addResourceLocations("file:"+filePath);

}
@Resource
private InterceptorCORS interceptorCORS;


@Override
public void addInterceptors(InterceptorRegistry registry) {
//添加跨域
registry.addInterceptor(interceptorCORS).addPathPatterns("/**");
}
}

官网使用addCorsMapping,但是限制太多,不推荐用

:::warning
注意:addCorsMapping 会被interceptor覆盖,后续如果添加自定义的拦截器(包括Spring security),addCorsMappings方法实现的统一跨域配置就会失效,其原因在于请求经过的先后顺序:

当请求到来时会先进入拦截器中,而不是进入Mapping映射中,所以返回的头信息中并没有配置的跨域信息。浏览器就会报跨域异常
:::
链接
这里可以使用allowedOriginsPattern(“*”)配置多个

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Configuration(proxyBeanMethods = false)
public class MyConfiguration implements WebMvcConfigurer {


public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("*") // 所有的外部域都可跨域访问。 如果是localhost则很难配置,因为在跨域请求的时候,外部域的解析可能是localhost、127.0.0.1、主机名
.allowCredentials(true) // 是否支持跨域用户凭证
.allowedMethods("*") // 当前站点支持的跨域请求类型是什么
.maxAge(3600); // 超时时长设置为1小时。 时间单位是秒。
}
};

}

Java日志框架

1.SLF4J(Simple logging Facade for Java)

意思为简单日志门面,它是把不同的日志系统的实现进行了具体的抽象化,只提供了统一的日志使用接口,使用时只需要按照其提供的接口方法进行调用即可,由于它只是一个接口,并不是一个具体的可以直接单独使用的日志框架,所以最终日志的格式、记录级别、输出方式等都要通过接口绑定的具体的日志系统来实现,这些具体的日志系统就有log4j,logback,java.util.logging等,它们才实现了具体的日志系统的功能。

如何使用SLF4J?

既然SLF4J只是一个接口,那么实际使用时必须要结合具体的日志系统来使用,我们首先来看SLF4J和各个具体的日志系统进行绑定时的框架原理图:

img

其实slf4j原理很简单,他只提供一个核心slf4j api(就是slf4j-api.jar包),这个包只有日志的接口,并没有实现,所以如果要使用就得再给它提供一个实现了些接口的日志包,比 如:log4j,common logging,jdk log日志实现包等,但是这些日志实现又不能通过接口直接调用,实现上他们根本就和slf4j-api不一致,因此slf4j又增加了一层来转换各日志实现包的使 用,当然slf4j-simple除外。其结构如下:
slf4j-api(接口层)
|
各日志实现包的连接层( slf4j-jdk14, slf4j-log4j)
|
各日志实现包

所以,结合各日志实现包使用时提供的jar包情况为

SLF4J和logback结合使用时需要提供的jar:slf4j-api.jar,logback-classic.jar,logback-core.jar

SLF4J和log4j结合使用时需要提供的jar:slf4j-api.jar,slf4j-log412.jar,log4j.jar

SLF4J和JDK中java.util.logging结合使用时需要提供的jar:slf4j-api.jar,slf4j-jdk14.jar

SLF4J和simple(SLF4J本身提供的一个接口的简单实现)结合使用时需要提供的jar:slf4j-api.jar,slf4j-simple.jar

当然还有其他的日志实现包,以上是经常会使用到的一些。
:::warning
注意,以上slf4j和各日志实现包结合使用时最好只使用一种结合,不然的话会提示重复绑定日志,并且会导致日志无法输出。
:::

***slf4j-api.jar:对外提供统一的日志调用接口,该接口具体提供的调用方式和方法举例说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Test {

  private static final Logger logger = LoggerFactory.getLogger(Tester.class); //通过LoggerFactory获取Logger实例

  public static void main(String[] args) {

//接口里的统一的调用方法,各具体的日志系统都有实现这些方法
  logger.info("testlog: {}", "test");

​ logger.debug("testlog: {}", "test");

​ logger.error("testlog: {}", "test");

​ logger.trace("testlog: {}", "test");

​ logger.warn("testlog: {}", "test");
  }
}

如果系统中之前已经使用了log4j做日志输出,想使用slf4j作为统一的日志输出,该怎么办呢?

如果之前系统中是单独使用log4j做为日志输出的,这时再想使用slf4j做为日志输出时,如果系统中日志比较多,此时更改日志输出方法肯定是不太现实的,这个时候就可以使用log4j-over-slf4j.jar将使用log4j日志框架输出的日志路由到slf4j上来统一采用slf4j来输出日志。

为什么要使用SLF4J?

  • slf4j是一个日志接口,自己没有具体实现日志系统,只提供了一组标准的调用api,这样将调用和具体的日志实现分离,使用slf4j后有利于根据自己实际的需求更换具体的日志系统,比如,之前使用的具体的日志系统为log4j,想更换为logback时,只需要删除log4j相关的jar,然后加入logback相关的jar和日志配置文件即可,而不需要改动具体的日志输出方法,试想如果没有采用这种方式,当你的系统中日志输出有成千上万条时,你要更换日志系统将是多么庞大的一项工程。如果你开发的是一个面向公众使用的组件或公共服务模块,那么一定要使用slf4的这种形式,这有利于别人在调用你的模块时保持和他系统中使用统一的日志输出。
  • slf4j日志输出时可以使用{}占位符,如,logger.info(“testlog: {}”, “test”),而如果只使用log4j做日志输出时,只能以logger.info(“testlog:”+”test”)这种形式,前者要比后者在性能上更好,后者采用+连接字符串时就是new 一个String 字符串,在性能上就不如前者。

2.log4j(log for java)

Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

如何使用?

  • 引入jar,使用log4j时需要的jar为:log4j.jar。
  • 定义配置文件log4j.properties或log4j.xml
  • 在具体的类中进行使用:
    • 在需要日志输出的类中加入:private static final Logger logger = Logger.getLogger(Tester.class); //通过Logger获取Logger实例
    • 在需要输出日志的地方调用相应方法即可:logger.debug(“System …..”)

关于如何单独使用log4j,建议详细阅读以下文章:

https://blog.csdn.net/u012422446/article/details/51199724

https://blog.csdn.net/azheng270/article/details/2173430/

http://shmilyaw-hotmail-com.iteye.com/blog/2410764

3.logback

logback同样是由log4j的作者设计完成的,拥有更好的特性,用来取代log4j的一个日志框架,是slf4j的原生实现(即直接实现了slf4j的接口,而log4j并没有直接实现,所以就需要一个适配器slf4j-log4j12.jar),logback一共有以下几个模块:

  • logback-core:其它两个模块的基础模块
  • logback-classic:它是log4j的一个改良版本,同时它完整实现了slf4j API使你可以很方便地更换成其它日志系统如log4j或JDK14 Logging
  • logback-access:访问模块与Servlet容器集成提供通过Http来访问日志的功能

同样,单独使用它时,需要引入以上jar,然后进行配置文件的配置,最后就是在相关类中进行使用,使用时加入以下语句:

1
2
3
  private final static Logger logger = LoggerFactory.getLogger(Test.class);

 logger.info("打印日志");

对于logback的使用,详细使用方法及配置推荐阅读以下文章:

https://www.cnblogs.com/warking/p/5710303.html

4.总结如下:

1、slf4j是java的一个日志门面,实现了日志框架一些通用的api,log4j和logback是具体的日志框架。

2、他们可以单独的使用,也可以绑定slf4j一起使用。

单独使用,分别调用框架自己的方法来输出日志信息。绑定slf4j一起使用。调用slf4j的api来输入日志信息,具体使用与底层日志框架无关(需要底层框架的配置文件)。显然不推荐单独使用日志框架。假设项目中已经使用了log4j,而我们此时加载了一个类库,而这个类库依赖另一个日志框架。这个时候我们就需要维护两个日志框架,这是一个非常麻烦的事情。而使用了slf4j就不同了,由于应用调用的抽象层的api,与底层日志框架是无关的,因此可以任意更换日志框架。

这篇文章主要是来帮大家理清 slf4j,log4j,logback之间的关系,对于使用及相关配置文件的详细配置不做说明,对于具体的使用参考文章末尾相关参考链接。

最后,送上slf4j和log4j整合使用时的一些参考链接

https://blog.csdn.net/javaloveiphone/article/details/52486257 (log桥接工具jcl-over-slf4j使用,项目排除commons-logging依赖的影响)

https://blog.csdn.net/minicto/article/details/52672472

https://blog.csdn.net/foreverling/article/details/51385128

https://www.cnblogs.com/junzi2099/p/7930268.html#_label6

https://www.cnblogs.com/chenhongliang/p/5312517.html (java常用日志框架介绍)

spring-openapi-ui使用

swagger似乎已经不再维护了,不推荐再使用了

1
2
3
4
5
6
7
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>


可以替换为

1
2
3
4
5
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>latest-version</version>
</dependency>

文档在这里
https://springdoc.org/#migrating-from-springfox

https://blog.csdn.net/m0_47333020/article/details/109776819

添加全局token

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

@Configuration
@OpenAPIDefinition(
info = @Info(
title = "Swagger3",
version = "1.0",
description = "Swagger3使用演示",
contact = @Contact(name = "TOM")
),
security = @SecurityRequirement(name = "token"),
externalDocs = @ExternalDocumentation(description = "参考文档",
url = "https://github.com/swagger-api/swagger-core/wiki/Swagger-2.X---Annotations"
)
)
@SecurityScheme(type = SecuritySchemeType.APIKEY, name = "token", in = SecuritySchemeIn.HEADER)

public class SwaggerConfig {
@Bean
public GroupedOpenApi docker() {
return GroupedOpenApi.builder()
.packagesToScan("com.example.springtransfer.controller")
.group("api")
.pathsToMatch("/**").build();


}


}

添加多个header

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

@Configuration
@OpenAPIDefinition(
info = @Info(
title = "Swagger3",
version = "1.0",
description = "Swagger3使用演示",
contact = @Contact(name = "TOM")
),
security ={ @SecurityRequirement(name = "usersOrigin"), @SecurityRequirement(name = "Authorization")},
externalDocs = @ExternalDocumentation(description = "参考文档",
url = "https://github.com/swagger-api/swagger-core/wiki/Swagger-2.X---Annotations"
)
)

@SecuritySchemes({@SecurityScheme(type = SecuritySchemeType.APIKEY, name = "Authorization", in = SecuritySchemeIn.HEADER), @SecurityScheme(type = SecuritySchemeType.APIKEY, name = "usersOrigin", in = SecuritySchemeIn.HEADER)})
public class SwaggerConfig {
@Bean
public GroupedOpenApi docker() {
return GroupedOpenApi.builder()
.packagesToScan("com.example.springtransfer.controller")
.group("api")
.pathsToMatch("/**").build();


}


}

添加参数注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

@Tag(name = "操作接口", description = "操作描述")
@RestController
@RequestMapping("hs")
public class HsApi {

@Resource
private HsService hsService;

@Resource
private HsTypeService hsTypeService;

@Operation(summary = "添加", description = "添加描述")
@Parameters({
@Parameter(name = "name", description = "名字", required = true),
@Parameter(name = "typeId", description = "类型ID", required = true)
})
@PutMapping("add")
public JSONObject add(String name, Long typeId) {
HsType hsType = hsTypeService.findById(typeId);
Hs hs = new Hs();
hs.setName(name);
hs.setType(hsType);
hs.setDateCreated(new Date());
hs = hsService.save(hs);
return JSONObject.parseObject(JSONObject.toJSONString(hs));
}

@Operation(summary = "获取")
@GetMapping("get")
public JSONObject get(@Parameter(name = "id", description = "数据ID") Long id) {
Hs hs = hsService.findById(id);
return JSONObject.parseObject(JSONObject.toJSONString(hs));
}
}

spring-security和shiro屏蔽了swagger-ui的api

对于spring-security

1
2
3
4
5
6
7
8
9
10
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/css/**", "/js/**", "/index.html", "/img/**", "/fonts/**", "/favicon.ico", "/verifyCode","/swagger-ui/*","/v2/api-docs/**","/v3/api-docs/**",
"/swagger-resources",
"/swagger-resources/**",
"/configuration/ui",
"/configuration/security",
"/swagger-ui.html/**",
"/webjars/**" );
}

对于shiro

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Bean("shiroFilter")
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
if (UtilValidate.isEmpty(loginpage)) loginpage = "login.html";
ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
shiroFilter.setSecurityManager(securityManager);
shiroFilter.setLoginUrl("/" + loginpage);
shiroFilter.setUnauthorizedUrl("/");

Map<String, String> filterMap = new LinkedHashMap<>();

filterMap.put("/pobstyle.css", "anon");
filterMap.put("/webjars/**", "anon");
filterMap.put("/statics/**", "anon");
filterMap.put("/" + loginpage, "anon");
filterMap.put("/sys/login", "anon");
filterMap.put("/favicon.ico", "anon");
filterMap.put("/captcha.jpg", "anon");



//swagger请求不拦截
filterMap.put("/swagger/**", "anon");
filterMap.put("/v3/api-docs", "anon");
filterMap.put("/swagger-ui.html", "anon");
filterMap.put("/swagger-resources/**", "anon");
shiroFilter.setFilterChainDefinitionMap(filterMap);

filterMap.put("/**", "authc");
shiroFilter.setFilterChainDefinitionMap(filterMap);
return shiroFilter;
}

springboot shiro swagger的项目,shiro已经对swagger的资源放行了,但是只要有@RequiresPermissions注解的controller,swagger就读取不到

需要设置aop

1
2
3
4
5
6
7
8
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator =
new DefaultAdvisorAutoProxyCreator();
defaultAdvisorAutoProxyCreator.setUsePrefix(true);

return defaultAdvisorAutoProxyCreator;
}

使用swaggerui

:::warning

不推荐使用

:::

swaggerui配置

添加swaggerui依赖

1
2
3
4
5
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>

在主类添加注解: @EnableOpenApi

最后添加配置类和拦截器类

配置类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

@Configuration
@EnableOpenApi
//@ComponentScan("com.qgzx.mapper")
public class SwaggerConfig {
@Bean
public Docket docker() {
// 构造函数传入初始化规范,这是swagger2规范
return new Docket(DocumentationType.OAS_30)
//apiInfo: 添加api详情信息,参数为ApiInfo类型的参数,这个参数包含了第二部分的所有信息比如标题、描述、版本之类的,开发中一般都会自定义这些信息
.apiInfo(apiInfo())
.groupName("group")
//配置是否启用Swagger,如果是false,在浏览器将无法访问,默认是true
.enable(true)
.select()
//apis: 添加过滤条件,
.apis(RequestHandlerSelectors.basePackage("cn.hellohao.controller"))
//paths: 这里是控制哪些路径的api会被显示出来,比如下方的参数就是除了/user以外的其它路径都会生成api文档

.build();
}

private ApiInfo apiInfo() {
Contact contact = new Contact("名字:name", "个人链接:http://xxx.xxx.com/", "邮箱:XXX");
return new ApiInfo(
"标题内容",
"描述内容",
"版本内容:v1.0",
"链接:http://terms.service.url/",
contact,
"许可:Apach 2.0 ",
"许可链接:XXX",
new ArrayList<>()
);
}
}

拦截器类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@Component
public class SwaggerUiWebMvcConfigurer implements WebMvcConfigurer {
private final String baseUrl;

public SwaggerUiWebMvcConfigurer(
@Value("${springfox.documentation.swagger-ui.base-url:}") String baseUrl) {
this.baseUrl = baseUrl;
}

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
String baseUrl = StringUtils.trimTrailingCharacter(this.baseUrl, '/');

registry.
addResourceHandler(baseUrl + "/swagger-ui/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/")
.resourceChain(false);
}

@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController(baseUrl + "/swagger-ui/")
.setViewName("forward:" + baseUrl + "/swagger-ui/index.html");
}

@Override
public void addCorsMappings(CorsRegistry registry) {
registry
.addMapping("/api/pet")
.allowedOrigins("http://editor.swagger.io");
registry
.addMapping("/v2/api-docs.*")
.allowedOrigins("http://editor.swagger.io");
}
}

配置token

待续…

java模板引擎

有几个常用的模板引擎

pebble

1
2
3
4
5
<dependency>
<groupId>io.pebbletemplates</groupId>
<artifactId>pebble-spring-boot-starter</artifactId>
<version>3.1.5</version>
</dependency>

thymeleaf

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

https://www.docs4dev.com/docs/zh/thymeleaf/3.0/reference/using_thymeleaf.html#introducing-thymeleaf

thymeleaf fragments
https://blog.csdn.net/weixin_41999594/article/details/117786639

freemarker

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>

velocity

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-velocity</artifactId>
</dependency>

人生名言@RIchKing

1,话别说太满,人别熟太快。

2,别人自嘲可以,但你千万别附和。

3,不要把别人想得太坏,也不要把别人想得太好,都是凡人。

4,可以嘲笑你的朋友,但不能嘲笑他喜欢的东西。

5,把每一句“我不会”都改成“我可以学”。

阅读全文 »

作者:中天月
链接:https://www.zhihu.com/question/26457306/answer/32844615

人们倾向于记住最先发生的事情和最后发生的事情。中间的事情记不清楚。所以,如果你要做自我介绍的话,最好做第一个或者最后一个。面试的时候,也是一样的。

如果你在酒吧或者前台工作,在你身后放一面镜子。这样的话,当顾客发脾气的时候,就能从镜子里看到自己的丑恶嘴脸。一面镜子可以显著降低他们无理取闹的概率。

报价之后,不再说话。如果你是做销售工作的,这项技巧很有用。在其他领域,这项技巧也很有用。我之前干过一份工作,是在一家体育馆卖会员卡。有个老家伙就是这么指导我的,他说,一旦你和顾客寒暄完毕,报出了你的价格。从此时开始,先开口的那个就输了。看起来好像毫无根据,但确实是这个样子的。通常会有很长时间的尴尬沉默,但是,最终,顾客会买的。

如果你问了别人一个问题,然后他们回答了一半,你等着,他们会说完的。只要等着,保持眼神接触,最终,他们会开口讲完的。

公开讲话或者蹦极之前这种会紧张的时刻,嚼口香糖就好了。据说是因为人类在危险的时候会自动停止咀嚼(吃东西),所以吃东西的时候就是安全的,大脑就是这么告诉你的。反正这招对我很管用。

人们最终记住的不是你说过的话,而是你让他们产生的感觉。几乎所有的人都喜欢谈论自己的事情,所以,多问问题。

当你学习新东西的时候,尝试着教给朋友们,或者让他们问你相关的问题。如果你能教给人一杯水,你自己一定会有一桶水。

如果你看到某人时,很开心,溢于言表的开心,那么他们以后看到你也会手舞足蹈的。第一次也许不是这样,但第二次一定是。

阅读全文 »
0%