学习sb
目录
三、application.yml与bootstrap.yml的区别
一、创建项目
修改使用版本
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.7.RELEASE</version>
<relativePath/><!--lookupparentfromrepository-->
</parent>
二、启动项目
1.项目启动报错:
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.7.3)
2022-09-02 14:30:34.633 INFO 21044 --- [ main] c.l.demo.SpringLoggingDemoApplication : Starting SpringLoggingDemoApplication using Java 1.8.0_192 on LAPTOP-EPEBDNL1 with PID 21044 (D:\IDEAWorkSpace\2022_process\spring-logging-demo\target\classes started by xhm in D:\IDEAWorkSpace\2022_process)
2022-09-02 14:30:34.635 INFO 21044 --- [ main] c.l.demo.SpringLoggingDemoApplication : No active profile set, falling back to 1 default profile: "default"
2022-09-02 14:30:34.985 INFO 21044 --- [ main] c.l.demo.SpringLoggingDemoApplication : Started SpringLoggingDemoApplication in 0.6 seconds (JVM running for 1.482)
Disconnected from the target VM, address: '127.0.0.1:63428', transport: 'socket'
2.方式一:
将生成的spring-boot-starter修改为spring-boot-starter-web
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
3.方式二
修改依赖之后依然不生效,重新配置maven,sync项目,重新启动成功
二、修改properties为yml配置
删除原有properties,创建yml文件
三、application.yml与bootstrap.yml的区别
bootstrap.yml
用来程序引导时执行,应用于更加早期配置信息读取。可以理解成系统级别的一些参数配置,这些参数一般是不会变动的。application.yml
可以用来定义应用级别的, 应用程序特有配置信息,可以用来配置后续各个模块中需使用的公共参数等- 若application.yml 和 bootstrap.yml 在同一目录下:bootstrap.yml 先加载,application.yml 后加载
- bootstrap.yml 用于application上下文的引导阶段。bootstrap.yml 由父Spring ApplicationContext加载
- 但是若
application.yml
与bootstrap
存在相同的配置项,还是会覆盖bootstrap
,而application.yml
里面的内容可以动态替换。- bootstrap.yml典型的应用场景
- 当使用 Spring Cloud Config Server (或者Spring Cloud Alibaba Nacos)配置中心时,这时必须将 spring.application.name 和 spring.cloud.config.server.git.uri(或者spring.cloud.nacos.config)配置在 bootstrap.yml 配置文件中,添加连接到配置中心的配置属性来加载外部配置中心的配置信息
- 一些固定不变的属性
- 一些加密/解密的场景
————————————————
版权声明:本文为优快云博主「qq_三哥啊」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.youkuaiyun.com/qq_27579471/article/details/123440601
四、SpringBoot+mybatis-plus
1.pom文件
<!--数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1</version>
</dependency>
<!--自动生成器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.3.1</version>
</dependency>
<!--druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<!--velocity模板引擎-->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
2.application.yml文件配置
#application.yml 可以用来定义应用级别的, 应用程序特有配置信息,可以用来配置后续各个模块中需使用的公共参数等。如果搭配 spring-cloud-config 使用, application.yml里面定义的文件可以实现动态替换。
spring:
profiles:
active: dev
# mybatis- plus配置
mybatis-plus:
# xml扫描,多个目录用逗号或者分号隔开隔开
mapper-locations: classpath:mapper/*.xml
# 以下配置均有默认值,可以不设置
global-config:
db-config:
#主键类型 AUTO:"数据库ID自增" INPUT:"用户输入ID",ID_WORKER:"全局唯一ID (数字类型唯一ID)", UUID:"全局唯一ID UUID";
id-type: auto
configuration:
# 是否开启自动驼峰命名规则映射:从数据库列名到Java属性驼峰命名的类似映射
map-underscore-to-camel-case: true
# 返回map时true:当查询数据为空时字段返回为null,false:不加这个查询数据为空时,字段将被隐藏
call-setters-on-nulls: true
# 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
3.application-dev.yml配置
spring:
datasource:
druid:
url: jdbc:mysql://127.0.0.1:3306/db_ceshi?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&autoReconnect=true
# url: jdbc:mysql://192.168.0.136:3306/csg_cloud_system?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&autoReconnect=true
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
initial-size: 10
max-active: 100
min-idle: 10
max-wait: 60000
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 20
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 300000
test-while-idle: true
test-on-borrow: false
test-on-return: false
stat-view-servlet:
enabled: true
url-pattern: /druid/*
filter:
stat:
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: false
wall:
config:
multi-statement-allow: true
4.mybatis-plus自动生成
package com.logging.demo.configuration;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
/**
* @author :Sur chemin
* @date : 2022/9/2
*/
@Component
public class CodeGenerator {
String author;
String moduleName;
String parentPackage;
String tableNames;
String tablePrefix;
@Value("${spring.datasource.druid.url}")
String url;
@Value("${spring.datasource.druid.driver-class-name}")
String driverName;
@Value("${spring.datasource.druid.username}")
String username;
@Value("${spring.datasource.druid.password}")
String password;
// entiry 的父类
private final String superEntity = "com.logging.demo.entity.SuperEntity";
public void execute(){
// 代码生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java");
gc.setAuthor(author);
gc.setOpen(false);
gc.setSwagger2(true);
gc.setDateType(DateType.ONLY_DATE);
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dc = new DataSourceConfig();
dc.setUrl(url);
dc.setDriverName(driverName);
dc.setPassword(password);
dc.setUsername(username);
mpg.setDataSource(dc);
// 包配置
PackageConfig pc = new PackageConfig();
pc.setModuleName(moduleName);
pc.setParent(parentPackage);
mpg.setPackageInfo(pc);
// 自定义配置
// 添加自定义配置 使得数据表映射已存在的情况下 更新entity
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
// to do nothing
}
};
// 如果模板引擎是freemarker
// String templatePath = "templates/mapper.xml.ftl";
// 如果模板引擎是velocity
String templatePath = "/templates/mapper.xml.vm";
// 自定义输出配置
List<FileOutConfig> focList = new ArrayList<>();
// 自定义配置会被优先输出
focList.add(new FileOutConfig(templatePath) {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定义输出文件名,如果Entity设置了前后缀、此处主要xml的名称会跟着发生变化
String s = projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
+ "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
return s;
}
});
/*ic.setFileCreate(new IFileCreate() {
@Override
public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) {
// 判断自定义文件夹是否需要创建
checkDir("调用默认方法创建的目录,自定义目录用");
if (fileType == FileType.MAPPER) {
// 已经生成 mapper文件判断存在,不想重新生成返回 false
return !new File(filePath).exists();
}
// 允许生成模板文件
return true;
}
});*/
/*cfg.setFileCreate((configBuilder, fileType, filePath) -> {
//如果是Entity则直接返回true表示写文件
if (fileType == FileType.ENTITY) {
return true;
}
//否则先判断文件是否存在
File file = new File(filePath);
boolean exist = file.exists();
if (!exist) {
file.getParentFile().mkdirs();
}
//文件不存在或者全局配置的fileOverride为true才写文件
return !exist || configBuilder.getGlobalConfig().isFileOverride();
});*/
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
// 配置模板
TemplateConfig templateConfig = new TemplateConfig();
// 配置自定义输出模板
// 指定自定义模板路径,主要不用带上.ftl/.vm,会根据使用的模板引擎自动识别
// templateConfig.setEntity("templates/entity2.java");
// templateConfig.setService();
// templateConfig.setController();
templateConfig.setXml(null);
mpg.setTemplate(templateConfig);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
strategy.setSuperEntityClass(superEntity);
strategy.setEntityLombokModel(true);
strategy.setRestControllerStyle(true);
// 公共父类
// strategy.setSuperControllerClass(superController);
// 写于父类中的公共字段
strategy.setSuperEntityColumns("id", "ID");
strategy.setInclude(tableNames.split(","));
strategy.setControllerMappingHyphenStyle(true);
strategy.setTablePrefix(tablePrefix);
mpg.setStrategy(strategy);
// Velocity是默认模板引擎,不需要配置
// mpg.setTemplateEngine(new FreemarkerTemplateEngine());
mpg.execute();
}
public CodeGenerator setAuthor(String author) {
this.author = author;
return this;
}
public CodeGenerator setModuleName(String moduleName) {
this.moduleName = moduleName;
return this;
}
public CodeGenerator setParentPackage(String parentPackage) {
this.parentPackage = parentPackage;
return this;
}
public CodeGenerator setTableNames(String tableNames) {
this.tableNames = tableNames;
return this;
}
public CodeGenerator setTablePrefix(String tablePrefix) {
this.tablePrefix = tablePrefix;
return this;
}
}
测试类
package com.logging.demo;
import com.logging.demo.configuration.CodeGenerator;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.Arrays;
import java.util.List;
/**
* @author :Sur chemin
* @date : 2022/9/2
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = SpringLoggingDemoApplication.class)
public class GenertorTest {
@Autowired
private CodeGenerator codeGenerator;
@Test
public void test() {
List<String> asList = Arrays.asList(new String[]{"pro_change_order"});
asList.forEach(e -> {
codeGenerator.setAuthor("Sur chemin");
codeGenerator.setModuleName("");
codeGenerator.setParentPackage("com.logging.demo.project");
codeGenerator.setTableNames(e);
codeGenerator.setTablePrefix("pro_");
codeGenerator.execute();
});
}
}
5.分页插件
package com.logging.demo.configuration;
import com.baomidou.mybatisplus.core.parser.ISqlParser;
import com.baomidou.mybatisplus.extension.parsers.BlockAttackSqlParser;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize;
import com.google.common.collect.Lists;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.annotation.Order;
import java.util.List;
/**
* @author :Sur chemin
* @date : 2022/9/2
*/
@Configuration
//@MapperScan(basePackageClasses = Mc.class)
@MapperScan("com.logging.demo")
public class MybatisPlusConfig {
/**
* 分页插件
* @return
*/
@Bean
@Order(2)
public PaginationInterceptor paginationInterceptor(){
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
List<ISqlParser> sqlParserList = Lists.newArrayList();
// 攻击 SQL阻断解析器、加入解析链
sqlParserList.add(new BlockAttackSqlParser());
paginationInterceptor.setSqlParserList(sqlParserList);
// 单页限制500条,小于0 如-1不受限制
paginationInterceptor.setLimit(-1);
paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
// 设置方言
paginationInterceptor.setDialectType("sqlite");
return paginationInterceptor;
}
@Bean
@Primary
public CommonMetaObjectHandler commonMetaObjectHandler(){
return new CommonMetaObjectHandler();
}
}
6.自动填充类
package com.logging.demo.configuration;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import java.util.Date;
/**
* 通用填充类 适用于mybatis plus
* @author :Sur chemin
* @date : 2022/9/2
*/
public class CommonMetaObjectHandler implements MetaObjectHandler {
/**
* 创建人
*/
private final String createBy = "createBy";
/**
* 更新人
*/
private final String updateBy = "updateBy";
/**
* 创建时间
*/
private final String createDate = "createDate";
/**
* 更新时间
*/
private final String updateDate = "updateDate";
private final String delteFlag = "delteFlag";
/**
* 3.0.x新版自动填充api 判断实体类字段是否为null
* 为null,才执行自动填充
* 所以改回旧版过时api
* @param metaObject
*/
@Override
public void insertFill(MetaObject metaObject) {
setInsertFieldValByName(delteFlag, 0, metaObject);
setInsertFieldValByName(createDate,new Date(System.currentTimeMillis()), metaObject);
// 其他需要自动填充的字段在这里补充
}
@Override
public void updateFill(MetaObject metaObject) {
setInsertFieldValByName(delteFlag, 0, metaObject);
setInsertFieldValByName(createDate,new Date(System.currentTimeMillis()), metaObject);
}
}
五、SpringBoot+Swagger
1.pom文件
<!--Swagger-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
2.swagger配置类
package com.logging.demo.configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.ArrayList;
import java.util.List;
@Configuration
@EnableSwagger2
public class Swagger2Config {
private Boolean enabled = true;
@Bean
public Docket createRestApi(){
return new Docket(DocumentationType.SWAGGER_2)
.enable(enabled)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.logging.demo.controller"))
.paths(PathSelectors.any())
.build();
// .securitySchemes(securitySchemes())
// .securityContexts(securityContexts());
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("管理模块")
.description("管理模块")
.contact("")
.version("1.0")
.build();
}
private List<ApiKey> securitySchemes() {
//设置请求头信息
List<ApiKey> result = new ArrayList<>();
ApiKey apiKey = new ApiKey("Authorization", "Authorization", "header");
result.add(apiKey);
return result;
}
private List<SecurityContext> securityContexts() {
//设置需要登录认证的路径
List<SecurityContext> result = new ArrayList<>();
//result.add(getContextByPath("/brand/.*"));
return result;
}
private SecurityContext getContextByPath(String pathRegex){
return SecurityContext.builder()
.securityReferences(defaultAuth())
.forPaths(PathSelectors.regex(pathRegex))
.build();
}
private List<SecurityReference> defaultAuth() {
List<SecurityReference> result = new ArrayList<>();
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
result.add(new SecurityReference("Authorization", authorizationScopes));
return result;
}
}
controller测试类
package com.logging.demo.controller;
import io.swagger.annotations.Api;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* <p>
* 项目变更工单表 前端控制器
* </p>
*
* @author Sur chemin
* @since 2022-09-02
*/
@RestController
@Api(tags = "test")
@RequestMapping("/test")
public class TestController {
}
测试,不显示controller,原因没有在测试类中写接口方法,GET/POST
修改
package com.logging.demo.controller;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* <p>
* 项目变更工单表 前端控制器
* </p>
*
* @author Sur chemin
* @since 2022-09-02
*/
@RestController
@Api(tags = "test")
@RequestMapping("/test")
public class TestController {
@ApiOperation(value = "查询")
@GetMapping("/test")
public String test(){
return "1";
}
}
六、 SpringBoot+logback
七、SpringBoot+SpringSecurity
学习参考文章连接:https://blog.youkuaiyun.com/LC_Liangchao/article/details/123425625
Spring Security提供有若干个过滤器,它们能够拦截Servlet请求,并将这些请求转给认证和访问决策管理器处理,从而增强安全性。根据自己的需要,可以使用适当的过滤器来保护自己的应用程序
1.认证
2.授权
1.pom依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2.编写UserDetailsService的实现类
AuthenticationManager进行权限认证(最终走自定义的UserDetailsService的实现类的loadUserByUsername接口根据用户名查询数据库验证用户是否存在)
package com.logging.demo.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.logging.demo.auth.AdminUserDetails;
import com.logging.demo.entity.SysUser;
import com.logging.demo.mapper.SysUserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
/**
* @author :Sur chemin
* @date : 2022/9/6
*/
/**
* 加载用户特定数据的核心接口。里面定义了一个根据用户名查询用户信息的方法
*/
/**
* 实际开发中会把5.1这一步查询数据来判断用户名密码是否正确,所以我们需要改变的地方是:写一个UserDetailsService的实现类,
* 让DaoAuthenticationProvider去调用这个实现类。
* 自定义userDetailsService的实现类;查询数据库
*/
@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService{
@Autowired
private SysUserMapper sysUserMapper;
/**
* 登录请求 :1.查询根据用户名数据库用户是否存在
*
* @param username
* @return
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 校验用户名和密码
LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(SysUser::getUserName, username);
SysUser sysUser = sysUserMapper.selectOne(queryWrapper);
if(sysUser==null){
throw new RuntimeException("用户名或密码不正确");
}
// TODO 2. 存储用户信息进入SecurityContextHolder(包括权限信息)
return new AdminUserDetails(sysUser);
}
}
3.登录接口
package com.logging.demo.controller;
import com.logging.demo.common.CommonResult;
import com.logging.demo.entity.vo.SysUserReqVO;
import com.logging.demo.service.ISysUserService;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
/**
* <p>
* 前端控制器
* </p>
*
* @author Sur chemin
* @since 2022-09-06
*/
@RestController
@RequestMapping("/sys-user")
public class SysUserController {
@Autowired
ISysUserService sysUserService;
@ApiOperation("用户注册")
@PostMapping("addUser")
public CommonResult addUser(@RequestBody SysUserReqVO sysUserReqVO){
return sysUserService.addUser(sysUserReqVO);
}
@ApiOperation("用户登录")
@PostMapping("login")
public Map<String, Object> login(@RequestParam String username,
@RequestParam String password){
return sysUserService.login(username, password);
}
/**
* 根据手机号注册,手机号唯一
* @param mobile
* @param password
* @return
*/
@ApiOperation("修改/重置密码")
@PostMapping("reset")
public CommonResult reset(@RequestParam String mobile,
@RequestParam(required = false) String password){
return sysUserService.reset(mobile, password);
}
}
4.定义SpringSecurity配置类
我们一般使用springsecurity为我们提供的BCryptPasswordEncoder(内部会生成一个随机的盐,保证每次加密的结果都不一样)
package com.logging.demo.auth;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
/**
* @author :Sur chemin
* @date : 2022/9/6
*/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired(required = false)
private DynamicSecurityService dynamicSecurityService;
/**
* 在SecurityConfig里面配置认证的配置
*
* @param httpSecurity
* @throws Exception
*/
@Autowired
UserDetailsService userDetailsService;
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
/*// 所有请求必须认证通过
httpSecurity
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
//下边的路径放行
.antMatchers("/sys-user/login","/v2/api-docs", "/swagger-resources/configuration/ui",
"/swagger-resources","/swagger-resources/configuration/security",
"/swagger-ui.html","/webjars/**","/sys-user/addUser").permitAll()
.anyRequest().authenticated();*/
// 不拦截任何请求
/* httpSecurity
.authorizeRequests().anyRequest().permitAll();*/
/**
* 配置 参考文章
* https://blog.youkuaiyun.com/m0_56368068/article/details/125614281
*/
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests();
//不需要保护的资源路径允许访问
for (String url : ignoreUrlsConfig().getUrls()) {
registry.antMatchers(url).permitAll();
}
//允许跨域请求的OPTIONS请求
registry.antMatchers(HttpMethod.OPTIONS).permitAll();
// 任何请求需要身份认证
registry.and().authorizeRequests()//对url访问控制授权
.antMatchers("/actuator", "/actuator/*").authenticated()//需要进行认证
.antMatchers("/*").permitAll()// 在实际项目中经常需要放行所有静态资源,需要放行的url。
.anyRequest().authenticated()//anyRequest匹配所有请求,全部内容都需要认证
// 关闭跨站请求防护及不使用session
.and()
.csrf()
.disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
/**
* 自定义权限拒绝处理类
* spring Security 认证授权相关的异常进行统一的自定义处理
*/
.and().exceptionHandling()
.accessDeniedHandler(restfulAccessDeniedHandler()).authenticationEntryPoint(restAuthenticationEntryPoint())
// 自定义权限拦截器JWT过滤器
.and()
//.addFilterBefore(verifyCodeFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
//有动态权限配置时添加动态权限校验过滤器
if (dynamicSecurityService != null) {
registry.and().addFilterBefore(dynamicSecurityFilter(), FilterSecurityInterceptor.class);
}
}
/**
* 解决错误:ERROR:Encoded password does not look like BCrypt
* 原因:密码格式不匹配导致的
* @param auth
* @throws Exception
*
*
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
/**
* ERROR:WebSecurityConfigurerAdapter$UserDetailsServiceDelegator.loadUserByUsername(WebSecurityConfigurerAdapter.java:451)
* java.lang.StackOverflowError: null
* 没有执行UserDetailsService的实现类的loadUserByUsername方法,执行上面的loadUserByUsername方法递归调用导致栈溢出
*/
// auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder());
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
/**
* 登录接口
* 开放这个接口的白名单,让用户访问这个接口的时候不用登录也能访问。
* 在接口中我们通过AuthenticationManager的authenticate方法来进行用户认证,所以需要SecurityConfig中配置把AuthenticationManager注入容器。
* 认证成功的话要生成一个jwt,放入响应中返回,并且为了让用户回请求时能通过jwt识别出具体的是哪个用户,我们需要把用户信息存储入redis,用户id作为key
*
* @return
* @throws Exception 把AuthenticationManager注入容器中
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
// 替换掉默认的密码加密器
/**
* 我们一般使用springsecurity为我们提供的BCryptPasswordEncoder(内部会生成一个随机的盐,保证每次加密的结果都不一样)。
* 我们只需要使用吧BCryptPasswordEncoder对象注入spring容器中,springsecurity就会使用该passwordEncoder来进行密码校验。
* <p>
* 我们可以定义一个springsecurity的配置类,springsecurity要求这个配置类要继承WebSecurityConfigurerAdapter。然后在注册的时候,注入这个对象,给密码加密存储进数据库
*
* @return
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public JwtTokenUtil jwtTokenUtil() {
return new JwtTokenUtil();
}
/**
* 配置不拦截的请求路径,放行
* @return
*/
@Bean
public IgnoreUrlsConfig ignoreUrlsConfig() {
return new IgnoreUrlsConfig();
}
/**
* 自定义一个过滤器,这个过滤器会去获取请求头中的token,对token进行解析去除其中的userid
* 使用userid去redis中获取对于的 AdminUserDetails 对象
* 然后封装Authentication对象存入SecurityContextHolder
* @return
*/
@Bean
public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter() {
return new JwtAuthenticationTokenFilter();
}
@Bean
public RestfulAccessDeniedHandler restfulAccessDeniedHandler() {
return new RestfulAccessDeniedHandler();
}
@Bean
public RestAuthenticationEntryPoint restAuthenticationEntryPoint() {
return new RestAuthenticationEntryPoint();
}
@ConditionalOnBean(name = "dynamicSecurityService")
@Bean
public DynamicAccessDecisionManager dynamicAccessDecisionManager() {
return new DynamicAccessDecisionManager();
}
@ConditionalOnBean(name = "dynamicSecurityService")
@Bean
public DynamicSecurityFilter dynamicSecurityFilter() {
return new DynamicSecurityFilter();
}
}
ERROR:Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2022-09-08 18:08:09.651 ERROR 20204 --- [ main] o.s.boot.SpringApplication : Application run failedorg.springframework.context.ApplicationContextException: Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException
at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:181)
at org.springframework.context.support.DefaultLifecycleProcessor.access$200(DefaultLifecycleProcessor.java:54)
at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:356)
at java.lang.Iterable.forEach(Iterable.java:75)
at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:155)
at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:123)
at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:935)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:586)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:745)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:420)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:307)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1317)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1306)
at com.logging.demo.SpringLoggingDemoApplication.main(SpringLoggingDemoApplication.java:13)
Caused by: java.lang.NullPointerException: null
at springfox.documentation.spi.service.contexts.Orderings$8.compare(Orderings.java:112)
at springfox.documentation.spi.service.contexts.Orderings$8.compare(Orderings.java:109)
at com.google.common.collect.ComparatorOrdering.compare(ComparatorOrdering.java:37)
at java.util.TimSort.countRunAndMakeAscending(TimSort.java:355)
at java.util.TimSort.sort(TimSort.java:220)
at java.util.Arrays.sort(Arrays.java:1438)
at com.google.common.collect.Ordering.sortedCopy(Ordering.java:855)
at springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider.requestHandlers(WebMvcRequestHandlerProvider.java:57)
at springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper$2.apply(DocumentationPluginsBootstrapper.java:138)
at springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper$2.apply(DocumentationPluginsBootstrapper.java:135)
at com.google.common.collect.Iterators$7.transform(Iterators.java:750)
at com.google.common.collect.TransformedIterator.next(TransformedIterator.java:47)
at com.google.common.collect.TransformedIterator.next(TransformedIterator.java:47)
at com.google.common.collect.MultitransformedIterator.hasNext(MultitransformedIterator.java:52)
at com.google.common.collect.MultitransformedIterator.hasNext(MultitransformedIterator.java:50)
at com.google.common.collect.ImmutableList.copyOf(ImmutableList.java:249)
at com.google.common.collect.ImmutableList.copyOf(ImmutableList.java:209)
at com.google.common.collect.FluentIterable.toList(FluentIterable.java:614)
at springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper.defaultContextBuilder(DocumentationPluginsBootstrapper.java:111)
at springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper.buildContext(DocumentationPluginsBootstrapper.java:96)
at springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper.start(DocumentationPluginsBootstrapper.java:167)
at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:178)
... 14 common frames omittedDisconnected from the target VM, address: '127.0.0.1:52192', transport: 'socket'
原因:使用springboot2.6.0后,配置swagger,不论是2.9.2还是3.0.0都报错
解决方法:配置文件中加入 .yml 配置
spring:
mvc:
pathmatch:
matching-strategy: ant_path_matcher
总结
提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。