0 引言
1 springboot 整合 swagger2 ,并配置密码登录认证
依赖引入
spring-boot.version
:2.3.12.RELEASE
swagger.version
:1.5.14
springfox-swagger2.version
:2.8.0
<!-- swagger | start -->
<!-- springfox-swagger2 : 内部依赖/自动引入 swagger-annotations / swagger-models -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
<version>${springfox-swagger2.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${springfox-swagger2.version}</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>${swagger.version}</version>
</dependency>
<!-- swagger | end -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot.version}</version>
<exclusions>
<exclusion>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
</exclusion>
<!-- 去掉默认日志配置logback -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 模板引擎 -->
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-thymeleaf -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<version>${spring-boot.version}</version>
</dependency>
配置文件: application.yml
# swagger-doc
swagger:
# requestHander 的包路径
package: cn.xxx.yyy.app
title: project API
description: project API
version: 1.0
# 登录认证的账户密码
username: ${SWAGGER_USERNAME:admin}
password: ${SWAGGER_PASSWORD:admin}
contactAuthor: jack
contactUrl: localhost:${server.port}/swagger-ui.html
contactEmail: 1111@qq.com
spring:
resources:
# 静态资源的路径
static-locations: classpath:/static
thymeleaf:
cache: false
prefix: classpath:/templates/
encoding: UTF-8 #编码
suffix: .html #模板后缀
mode: HTML #模板
注:需配置静态路径的目录(
spring.resources.static-locations
)、模板引擎的策略(spring.thymeleaf
)
启用 Swagger2
方式1:@EnableSwagger2
注解
@EnableSwagger2
是springfox
提供的一个注解,代表swagger2
相关技术开启。启用后,会自动扫描当前类所在包,及子包中所有类型的swagger
相关注解,做swagger
文档的定制
//@EnableFeignClients // [feign] Feign 客户端调用第三方接口,本工程暂不涉及
@EnableDiscoveryClient(autoRegister = true) // [nacos] 注册中心 | feign 整合 nacos 需要启用此注解(服务提供端 + Feign 客户端) | true: 开启服务自动注册功能,项目启动后能在 nacos 的web端界面看到服务的相关信息,且具备拉取服务信息的功能(前提是 nacos.discovery.enabled 不为 false)
@EnableSwagger2 // [swagger] 开启swagger2
@Api(tags = "yyyy app 启动模块") // [swagger] 作用范围: 模块类 | tags = "模块说明"
@MapperScan("cn.seres.bd.datasource.app") // [mybatis]
@ServletComponentScan(basePackages = "cn.xxx.yyy.app")// 扫描 Servlet 包,例如: ApplicationRequestListener
@SpringBootApplication // [spring-boot]
//@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@RestController // // [spring-web(mvc / webflux)]
//@RequestMapping("/") // [spring-web(mvc / webflux)]
@ComponentScan(basePackages = {"cn.xxx.yyy"}) // [spring-context]
public class DataSourceApplication {
...
}
方式2:配置类 : Swagger2
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import springfox.documentation.RequestHandler;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import java.util.ArrayList;
import java.util.List;
//@EnableSwagger2 此处,也可利用注解
@Configuration
public class Swagger2Configuration implements WebMvcConfigurer {
private static final String splitor = ";";
//可以获取application.yml 配置的参数
@Autowired
public Environment env;
@Autowired
private ApplicationContext applicationContext;
@Bean
public Docket api() {
//添加 head 参数 start
ParameterBuilder tokenPar = new ParameterBuilder();
List<Parameter> params = new ArrayList<Parameter>();
//这里配置 访问jwt令牌请求头,不需要的不加
/**
params.add(
new ParameterBuilder()
.name("authorization")
.description("令牌")
.modelRef(new ModelRef("string"))
.parameterType("header")
.required(false)
.build()
);
**/
return new Docket(DocumentationType.SWAGGER_2)
.select()
//.apis(RequestHandlerSelectors.any())
.apis(RequestHandlerSelectors.basePackage(env.getProperty("swagger.package")))
//.apis(basePackage("com.tyzn.controller.api" + splitor + "com.tyzn.controller.backApi"))
.paths(PathSelectors.any())
//.paths(Predicates.not(PathSelectors.regex("/error.*")))//过滤掉 error* 页面
//.paths(Predicates.or(PathSelectors.regex("/error.*"), PathSelectors.regex("/admin/.*") ) ) //过滤掉 error* 页面或 /admin/ 路径的请求
.build()
.globalOperationParameters(params)
.apiInfo(apiInfo());
}
private ApiInfo apiInfo() {
ApiInfoBuilder ab = new ApiInfoBuilder();
ab.title(env.getProperty("swagger.title"));
ab.description(env.getProperty("swagger.description"));
ab.version(env.getProperty("swagger.version"));
ab.contact(new Contact(env.getProperty("swagger.contactAuthor"), env.getProperty("swagger.contactUrl"), env.getProperty("swagger.contactEmail")));
//ab.contact(new Contact("Jack", "localhost:8080/swagger-ui.html", "xxxx@qq.com"));
return ab.build();
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/");
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
public static Predicate<RequestHandler> basePackage(final String basePackage) {
return input -> declaringClass(input).transform(handlerPackage(basePackage)).or(true);
}
private static Function<Class<?>, Boolean> handlerPackage(final String basePackage) {
return input -> {
// 循环判断匹配
for (String strPackage : basePackage.split(splitor)) {
boolean isMatch = input.getPackage().getName().startsWith(strPackage);
if (isMatch) {
return true;
}
}
return false;
};
}
private static Optional<? extends Class<?>> declaringClass(RequestHandler input) {
return Optional.fromNullable(input.declaringClass());
}
}
登录过滤器 : SwaggerFilter
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* @author 闲走天涯
* @version V1.0
* @Title: Swagger2
* @Description: 登录过滤器
* @date 2021-08-05
*/
@Component
public class SwaggerFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
String requestURI = req.getRequestURI();
//swagger-security
if (requestURI.contains("swagger-ui")) {
//获取 session
HttpSession session = req.getSession();
//判断 swagger 是否已经登录
Object swaggerLogin = session.getAttribute("swaggerLogin");
if (swaggerLogin == null || StringUtils.isEmpty(swaggerLogin.toString()) || !Boolean.parseBoolean(swaggerLogin.toString())) {
String basePath = req.getScheme() + "://" + req.getServerName() + ":" + req.getServerPort() + req.getContextPath() + "/";
String fullPath = basePath + "swagger/toLogin";
//重定向登录页面
res.sendRedirect(fullPath);
} else {
chain.doFilter(req, res);
}
} else {
chain.doFilter(req, res);
}
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
}
控制器 : SwaggerController
- SwaggerController : swagger 登录控制层-登录接口
import cn.xxx.common.entity.SwaggerAccount;
import cn.xxx.model.dto.XResponse;
import com.alibaba.cloud.commons.lang.StringUtils;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
@Controller // 应该用@RestController, 如果是 @Controller,会认为你返回的一个 modelandview 要解析,即会报错"TemplateInputException: Error resolving template 无法解析页面"
@RequestMapping(value = "/swagger")
@Slf4j
public class SwaggerController {
@Value("${swagger.username}")
private String swaggerUserName;
@Value("${swagger.password}")
private String swaggerPassword;
@PostConstruct
public void init(){
log.info("swaggerUserName: {}", swaggerUserName);
log.info("swaggerPassword: {}", swaggerPassword);
}
@GetMapping(value = {"toLogin", ""})
public String toLogin(HttpServletRequest request) {
Object swaggerLogin = request.getSession().getAttribute("swaggerLogin");
if (swaggerLogin != null && StringUtils.isNotBlank(swaggerLogin.toString()) && Boolean.parseBoolean(swaggerLogin.toString())) {
return "redirect:/swagger-ui.html";
}
return "swaggerLogin";
}
@PostMapping(value = "/login")
@ResponseBody
public ResponseEntity<XResponse> login(@RequestBody SwaggerAccount swaggerAccount, HttpServletRequest request) {
log.info("swaggerAccount : {} , config.username : {}, config.password : {}", swaggerAccount, swaggerUserName, swaggerPassword);
if (
StringUtils.isNotBlank(swaggerAccount.getUsername()) && StringUtils.isNotBlank(swaggerAccount.getPassword())
&& swaggerUserName.equals(swaggerAccount.getUsername()) && swaggerPassword.equals(swaggerAccount.getPassword())
) {
request.getSession().setAttribute("swaggerLogin", true);
return ResponseEntity.ok( XResponse.success("ok") );
}
return ResponseEntity.ok( XResponse.failure("fail") );
}
}
- SwaggerAccount
import lombok.*;
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class SwaggerAccount {
private String username;
private String password;
}
- XResponse
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@ApiModel(value = "公共响应对象", description = "公共响应对象") // [swagger] 作用范围: 模型类,如VO、BO
public class XResponse<T> {
private static final long serialVersionUID = 1L;
/**
* 状态码
*/
@ApiModelProperty(
value = "响应状态",
example = "true",
hidden = false
) // [swagger] 作用范围: 类的方法、属性上 / hidden=true 可隐藏该属性 / value = "属性说明"
private Boolean status;
/**
* 返回对象
*/
@ApiModelProperty(value = "业务数据")
private T data;
/**
* 错误码
*/
@ApiModelProperty(value = "错误码")
private String errorCode;
/**
* 错误信息
*/
@ApiModelProperty(value = "错误信息", example = "error message")
private String errorMsg;
@ApiModelProperty(value = "traceId", example = "trace id")
private String traceId;
public XResponse() {
}
public XResponse(Boolean status, T data, String errorCode, String errorMsg) {
this.status = status;
this.data = data;
this.errorCode = errorCode;
this.errorMsg = errorMsg;
}
public static XResponse create(Boolean status, Object data, String errorCode,
String errorMsg) {
return new XResponse(status, data, errorCode, errorMsg);
}
public static XResponse success() {
return create(true, null, null, null);
}
public static <T> XResponse success(T obj) {
return create(true, obj, null, null);
}
public static XResponse failure() {
return create(false, null, null, null);
}
public static XResponse failure(String errorCode) {
return create(false, null, errorCode, null);
}
public static XResponse failure(String errorCode, String errorMsg) {
return create(false, null, errorCode, errorMsg);
}
public Boolean getStatus() {
return status;
}
public T getData() {
return data;
}
public String getErrorCode() {
return errorCode;
}
public String getErrorMsg() {
return errorMsg;
}
public void setStatus(Boolean status) {
this.status = status;
}
public void setData(T data) {
this.data = data;
}
public void setErrorCode(String errorCode) {
this.errorCode = errorCode;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
public String getTraceId() {
return traceId;
}
public void setTraceId(String traceId) {
this.traceId = traceId;
}
@Override
public String toString() {
return "XResponse{" +
"status=" + status +
", data=" + data +
", errorCode='" + errorCode + '\'' +
", errorMsg='" + errorMsg + '\'' +
", traceId='" + traceId + '\'' +
'}';
}
}
登录页面 : swagger-login.html
/resources/templates/swagger-login.html
<!DOCTYPE html>
<html lang="en">
<head>
<!-- https://www.runoob.com/html/html5-intro.html -->
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Login</title>
<style>
.login {
width: 100%;
height: 600px;
text-align: center;
display: flex;
justify-content: center;
align-items: center;
flex-flow: column;
}
.body > div {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.body > div > input {
margin-left: 10px;
}
</style>
</head>
<body>
<div class="login">
<h2 class="title">欢迎使用 SWAGGER </h2>
<div class="body">
<div>
<span>账号:</span>
<input type="text" id="username">
</div>
<div>
<span>密码:</span>
<input type="password" id="password">
</div>
</div>
<button type="button" id="logBtn">登陆</button>
</div>
<!-- 对应文件在服务器的物理存储路径: /resources/static/js/jquery-3.7.1.js -->
<script src="../js/jquery-3.7.1.js"></script>
<!--<script src="https://code.jquery.com/jquery-3.7.1.js"></script>-->
<script>
$('#logBtn').click(function () {
let username = $("#username").val();
let password = $("#username").val();
if (username === '' && password === '') {
alert('username and password must be not empty!');
return false;
}
$.ajax({
url: '/swagger/login',
//data: { username: username, password: password },
data : JSON.stringify({ username: username, password: password }),
type: 'post',
//contentType: 'application/x-www-form-urlencoded'
contentType: 'application/json',
//dataType: 'json',
success: function (result) {
if (result.status) {
location.href = '/swagger-ui.html';
} else {
alert(result.errorCode + ':' + result.errorMsg);
}
}
, error : function (result) {
console.log("error")
}
});
});
</script>
</body>
</html>
测试访问
- http://127.0.0.1:8080/swagger/toLogin
- http://127.0.0.1:8080/swagger
- http://127.0.0.1:8080/swagger-ui.html
X 参考文献
- springboot 整合 swagger2 配置账号密码登录 demo代码 - CSDN
- springboot整合spring security安全框架-简单验证账号密码 - CSDN
- SpringBoot项目中使用Swagger2及注解解释的详细教程 - jb51.net
- SpringBoot 教程核心功能-Web 开发(静态资源) - CSDN 【推荐】
- https://jquery.com
标签:Web,Swagger,return,String,文档,org,import,swagger,public From: https://www.cnblogs.com/johnnyzen/p/18303277"控制台日志输出告警,页面按F12打开swagger控制台"/","/csrf"两个404接口" => 在 2.9.x 版本中有,暂时还没有找到好的解决方案,回退到 2.8.0 版本可以解决