新增swagger和shardingjdbc

This commit is contained in:
zhuyijun 2022-09-23 01:00:22 +08:00
parent 4d7590a3c9
commit ee32e15a6e
48 changed files with 871 additions and 159 deletions

22
pom.xml
View File

@ -38,12 +38,13 @@
<feign-ribbon.version>11.8</feign-ribbon.version>
<!-- json解析 -->
<jackson.version>2.13.3</jackson.version>
<jackson-datatype-jsr310.version>2.13.3</jackson-datatype-jsr310.version>
<fastjson.version>2.0.11</fastjson.version>
<!-- ORM -->
<mybatis-plus-boot-starter.version>3.4.3</mybatis-plus-boot-starter.version>
<mysql-jdbc.version>8.0.21</mysql-jdbc.version>
<postgresql.version>42.5.0</postgresql.version>
<jackson-datatype-jsr310.version>2.13.3</jackson-datatype-jsr310.version>
<shardingsphere.version>4.1.1</shardingsphere.version>
<!-- spring-cloud-alibaba版本配置 -->
<aliyun.version>4.5.0</aliyun.version>
<com.alibaba.transmittable.version>2.12.2</com.alibaba.transmittable.version>
@ -65,7 +66,8 @@
<commons-collections4.version>4.4</commons-collections4.version>
<commons-io.version>2.11.0</commons-io.version>
<commons-crypto.version>1.1.0</commons-crypto.version>
<!-- 文档-->
<knife4j.version>3.0.3</knife4j.version>
</properties>
<dependencyManagement>
@ -122,6 +124,16 @@
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-jdbc.version}</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>${knife4j.version}</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-micro-spring-boot-starter</artifactId>
<version>${knife4j.version}</version>
</dependency>
<!-- JWT 依赖开始 -->
<!-- <dependency>-->
<!-- <groupId>io.jsonwebtoken</groupId>-->
@ -242,6 +254,12 @@
<!-- <artifactId>spring-security-oauth2-authorization-server</artifactId>-->
<!-- <version>${spring-authorization-server.version}</version>-->
<!-- </dependency>-->
<!-- https://mvnrepository.com/artifact/org.apache.shardingsphere/shardingsphere-jdbc-core-spring-boot-starter -->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>${shardingsphere.version}</version>
</dependency>
</dependencies>

View File

@ -37,7 +37,7 @@
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<artifactId>zyjblogs-web-spring-boot-starter</artifactId>
<artifactId>zyjblogs-common-spring-boot-starter</artifactId>
<groupId>cn.zyjblogs.starter</groupId>
<exclusions>
<exclusion>
@ -46,11 +46,11 @@
</exclusion>
</exclusions>
</dependency>
<!-- 集成redis-->
<!-- <dependency>-->
<!-- <groupId>cn.zyjblogs.starter</groupId>-->
<!-- <artifactId>zyjblogs-redis-spring-boot-starter</artifactId>-->
<!-- </dependency>-->
<!-- 集成redis-->
<!-- <dependency>-->
<!-- <groupId>cn.zyjblogs.starter</groupId>-->
<!-- <artifactId>zyjblogs-redis-spring-boot-starter</artifactId>-->
<!-- </dependency>-->
<!-- 集成nacos-->
<!-- 集成 Nacos 作为服务注册中心配置 -->
@ -85,5 +85,10 @@
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<!-- knife4j文档 -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,44 @@
package cn.zyjblogs.config.swagger;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
import springfox.documentation.swagger.web.UiConfiguration;
/**
* @author zhuyijun
*/
@RestController
@RequestMapping("/swagger-resources")
public class SwaggerHandler {
private final SwaggerResourcesProvider swaggerResources;
private final UiConfiguration uiConfiguration;
public SwaggerHandler(SwaggerResourcesProvider swaggerResources, UiConfiguration uiConfiguration) {
this.swaggerResources = swaggerResources;
this.uiConfiguration = uiConfiguration;
}
// @GetMapping("/swagger-resources/configuration/security")
// public Mono<ResponseEntity<SecurityConfiguration>> securityConfiguration() {
// return Mono.just(new ResponseEntity<>(
// Optional.ofNullable(OAuth2ResourceServerAutoConfiguration).orElse(SecurityConfigurationBuilder.builder().build()), HttpStatus.OK));
// }
@GetMapping("")
public Mono<ResponseEntity> swaggerResources() {
return Mono.just((new ResponseEntity<>(swaggerResources.get(), HttpStatus.OK)));
}
@GetMapping("/configuration/ui")
public Mono<ResponseEntity<UiConfiguration>> uiConfiguration() {
return Mono.just(new ResponseEntity<>(uiConfiguration, HttpStatus.OK));
}
}

View File

@ -0,0 +1,66 @@
package cn.zyjblogs.config.swagger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.config.GatewayProperties;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.support.NameUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import springfox.documentation.swagger.web.SwaggerResource;
import springfox.documentation.swagger.web.SwaggerResourcesProvider;
import springfox.documentation.swagger.web.UiConfiguration;
import springfox.documentation.swagger.web.UiConfigurationBuilder;
import java.util.ArrayList;
import java.util.List;
/**
* @author zhuyijun
*/
@Configuration
@Primary
public class SwaggerProvider implements SwaggerResourcesProvider {
public static final String API_URI = "/v2/api-docs";
private final RouteLocator routeLocator;
private final GatewayProperties gatewayProperties;
@Autowired
public SwaggerProvider(RouteLocator routeLocator, GatewayProperties gatewayProperties) {
this.routeLocator = routeLocator;
this.gatewayProperties = gatewayProperties;
}
@Override
public List<SwaggerResource> get() {
List<SwaggerResource> resources = new ArrayList<>();
List<String> routes = new ArrayList<>();
//取出gateway的route
routeLocator.getRoutes().subscribe(route -> routes.add(route.getId()));
//结合配置的route-路径(Path)和route过滤只获取有效的route节点
gatewayProperties.getRoutes().stream().filter(routeDefinition -> routes.contains(routeDefinition.getId()))
.forEach(routeDefinition -> routeDefinition.getPredicates().stream()
.filter(predicateDefinition -> ("Path").equalsIgnoreCase(predicateDefinition.getName()) && predicateDefinition.getArgs().get(NameUtils.GENERATED_NAME_PREFIX + "0").startsWith("/api"))
.forEach(predicateDefinition -> resources.add(swaggerResource(routeDefinition.getId(),
predicateDefinition.getArgs().get(NameUtils.GENERATED_NAME_PREFIX + "0")
.replace("/**", API_URI)))));
return resources;
}
private SwaggerResource swaggerResource(String name, String location) {
SwaggerResource swaggerResource = new SwaggerResource();
swaggerResource.setName(name);
swaggerResource.setLocation(location);
swaggerResource.setSwaggerVersion("3.0");
return swaggerResource;
}
@Bean
public UiConfiguration uiConfiguration() {
return UiConfigurationBuilder.builder().build();
}
}

View File

@ -1,9 +1,9 @@
package cn.zyjblogs.filter;
import cn.zyjblogs.starter.common.entity.constant.HttpHeaderConstant;
import cn.zyjblogs.starter.common.entity.response.HttpCode;
import cn.zyjblogs.starter.common.entity.response.ResponseObject;
import cn.zyjblogs.starter.common.entity.response.ResponseResult;
import cn.zyjblogs.starter.common.entity.response.HttpCode;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
@ -11,8 +11,6 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
@ -42,7 +40,6 @@ import java.util.List;
public class AuthFilter implements GlobalFilter {
private final WhiteListProperties whiteListProperties;
private AntPathMatcher antPathMatcher = new AntPathMatcher();
private String SIGNING_KEY="zyjblogs123";
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
@ -54,16 +51,12 @@ public class AuthFilter implements GlobalFilter {
ServerHttpRequest request = build.getRequest();
ServerHttpResponse response = build.getResponse();
String token = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
log.debug("token:{}", token);
String path = request.getURI().getPath();
String methodType = request.getMethodValue().toLowerCase();
Route route = (Route) exchange.getAttributes().get(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
String routeId = route.getId();
//白名单请求直接放行
if (isWhileList(path)) {
return chain.filter(exchange);
}
if (StringUtils.isEmpty(token)) {
if (token == null || StringUtils.isEmpty(token) || !token.startsWith(HttpHeaderConstant.AUTHORIZATION_TYPE)) {
return getErrorMono(response, HttpCode.UNAUTHORIZED, "无访问权限");
}
if (isExpired(token)) {
@ -93,15 +86,19 @@ public class AuthFilter implements GlobalFilter {
* @date 2021/11/15 19:17
*/
private boolean isExpired(String token) {
if (!token.startsWith(HttpHeaderConstant.AUTHORIZATION_TYPE)) {
return true;
}
// String jwt = token.replace(HttpHeaderConstant.AUTHORIZATION_TYPE + " ", "");
// OauthClaims body = (OauthClaims) Jwts.parser().setSigningKey(SIGNING_KEY).parseClaimsJws(jwt).getBody();
// BaseContextHandler.set(ContextDto.builder().token(jwt).userId(body.getUserId()).username(body.getUsername()).build());
//// String jwt = token.replace(HttpHeaderConstant.AUTHORIZATION_TYPE, "").trim();
// try {
//// PublicKey publicKeyByPath = RsaUtils.getPublicKeyByPath(rsaKeyProperties.getPubKeyPath());
//// Claims body = new JwtParsers(false).setSigningKey(publicKeyByPath).parseClaimsJwt(jwt).getBody();
//// log.info("{}", body);
// return false;
// } catch (Exception e) {
// return true;
// }
return false;
}
/**
* 是否是白名单
*
@ -121,7 +118,6 @@ public class AuthFilter implements GlobalFilter {
}
/**
* 验证不通过时的返回
*

View File

@ -7,6 +7,8 @@ import java.util.List;
/**
* 白名单
*
* @author zhuyijun
*/
@Data
@ConfigurationProperties(prefix = "zyjblogs.whilelist")

View File

@ -18,6 +18,7 @@ spring:
shared-configs[0]:
data-id: zyjblogs-global-${spring.profiles.active}.yml
group: global
refresh: true
discovery:
server-addr: ${spring.cloud.nacos.config.server-addr}
username: ${spring.cloud.nacos.config.username}

View File

@ -4,6 +4,7 @@ import cn.zyjblogs.config.handler.ResourceAccessDeniedHandler;
import cn.zyjblogs.config.handler.ResourceAuthenticationEntryPoint;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@ -12,14 +13,19 @@ import org.springframework.security.oauth2.config.annotation.web.configuration.R
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
/**
* @author zhuyijun
*/
@Configuration
@EnableResourceServer
@RequiredArgsConstructor
@EnableConfigurationProperties(WhiteListProperties.class)
@RefreshScope
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Value("${spring.application.name}")
private String resourceId;
private final TokenStore tokenStore;
private final WhiteListProperties whiteListProperties;
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
@ -45,10 +51,12 @@ public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
.csrf()
.disable()
//限制资源服务器作用范围为 "/user/**", "/demo/**"
.requestMatchers().antMatchers("/user/**", "/demo/**").and()
.requestMatchers().antMatchers("/webjars/**", "/swagger-ui.html/**", "/swagger-resources/**", "/v2/api-docs/**", "/user/**", "/demo/**",
String.join(",", whiteListProperties.getAllowPaths()))
.and()
.formLogin().and()
.authorizeRequests()
.antMatchers("/webjars/**", "/swagger-ui.html/**", "/swagger-resources/**", "/v2/api-docs/**")
.antMatchers("/webjars/**", "/swagger-ui.html/**", "/swagger-resources/**", "/v2/api-docs/**", String.join(",", whiteListProperties.getAllowPaths()))
.permitAll()
.antMatchers("/login", "/oauth/**", "/user/login", "/user/refresh/token").permitAll()
//以下请求必须认证通过

View File

@ -0,0 +1,18 @@
package cn.zyjblogs.config.security;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.List;
/**
* 白名单
*
* @author zhuyijun
*/
@Data
@ConfigurationProperties(prefix = "zyjblogs.whilelist")
public class WhiteListProperties {
private List<String> allowPaths;
}

View File

@ -6,6 +6,7 @@ import cn.zyjblogs.server.user.service.AuthService;
import cn.zyjblogs.server.user.vo.OAuth2AccessTokenVo;
import cn.zyjblogs.starter.common.entity.response.ResponseObject;
import cn.zyjblogs.starter.common.entity.response.ResponseResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
@ -17,10 +18,11 @@ import org.springframework.web.bind.annotation.RestController;
/**
* @author zhuyijun
*/
@Api(tags = {"token管理"})
@RestController
@RequiredArgsConstructor
@RequestMapping("/auth")
public class AutoController {
public class AuthController {
private final AuthService authService;
@ApiOperation(value = "刷新token", notes = "刷新token")

View File

@ -5,6 +5,7 @@ import cn.zyjblogs.server.user.service.LoginService;
import cn.zyjblogs.server.user.vo.OAuth2AccessTokenVo;
import cn.zyjblogs.starter.common.entity.response.ResponseObject;
import cn.zyjblogs.starter.common.entity.response.ResponseResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
@ -16,6 +17,7 @@ import org.springframework.web.bind.annotation.RestController;
/**
* @author zhuyijun
*/
@Api(tags = {"登录认证管理"})
@RestController
@RequiredArgsConstructor
@RequestMapping("/user")

View File

@ -21,6 +21,7 @@ spring:
shared-configs[0]:
data-id: zyjblogs-global-${spring.profiles.active}.yml
group: global
refresh: true
discovery:
server-addr: ${spring.cloud.nacos.config.server-addr}
username: ${spring.cloud.nacos.config.username}

View File

@ -8,6 +8,8 @@ import cn.zyjblogs.starter.common.entity.response.ResponseObject;
import cn.zyjblogs.starter.common.entity.response.ResponseResult;
import cn.zyjblogs.starter.common.utils.bean.BeanUtils;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@ -21,6 +23,7 @@ import java.util.List;
/**
* @author zhuyijun
*/
@Api(tags = {"角色管理"})
@RestController
@RequestMapping("/role")
@ResponseBody
@ -36,6 +39,7 @@ public class RoleController {
* @author zhuyijun
* @date 2022/9/19 上午12:06
*/
@ApiOperation(value = "1", notes = "通过id查询用户")
@GetMapping("/id")
public ResponseObject<RoleVo> findById(@RequestParam String id) {
RolePo byId = roleService.getById(id);
@ -49,12 +53,25 @@ public class RoleController {
* @author zhuyijun
* @date 2022/9/19 上午1:14
*/
@ApiOperation(value = "page=1&limit=10", notes = "分页查询")
@GetMapping("/page")
public ResponseObject<List<RoleVo>> findPage(RolePageDto rolePageDto) {
IPage<RoleVo> page = roleService.findPage(rolePageDto);
return ResponseResult.success(page.getTotal(), page.getRecords());
}
/**
* @param
* @return cn.zyjblogs.starter.common.entity.response.ResponseObject<java.util.List < cn.zyjblogs.server.role.vo.RoleVo>>
* @author zhuyijun
* @date 2022/9/22 下午11:33
*/
@ApiOperation(value = "", notes = "查询所有角色")
@GetMapping("/list")
public ResponseObject<List<RoleVo>> findList() {
return ResponseResult.success(roleService.findList());
}
/**
* 通过id集合查询
*
@ -63,6 +80,7 @@ public class RoleController {
* @author zhuyijun
* @date 2022/9/19 上午1:14
*/
@ApiOperation(value = "1", notes = "通过角色id查询")
@PostMapping("/findByIds")
public ResponseObject<List<RoleVo>> findByIds(List<String> ids) {
return ResponseResult.success(roleService.findByIds(ids));

View File

@ -1,7 +1,13 @@
package cn.zyjblogs.server.role.dto;
import cn.zyjblogs.starter.common.entity.constant.CommonConstant;
import cn.zyjblogs.starter.common.entity.dto.PageDto;
import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
@ -28,12 +34,18 @@ public class RolePageDto extends PageDto implements Serializable {
private String createUserId;
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JSONField(format = CommonConstant.DATETIME_PATTERN)
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = CommonConstant.DATETIME_PATTERN)
private LocalDateTime createTime;
private String editUserId;
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JSONField(format = CommonConstant.DATETIME_PATTERN)
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = CommonConstant.DATETIME_PATTERN)
private LocalDateTime editTime;
private String tenantId;

View File

@ -1,11 +1,12 @@
package cn.zyjblogs.server.role.po;
import com.alibaba.fastjson.annotation.JSONField;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import lombok.AllArgsConstructor;
import lombok.Builder;
@ -40,6 +41,7 @@ public class RolePo implements Serializable {
private String createUserId;
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField("create_time")
private LocalDateTime createTime;
@ -48,9 +50,11 @@ public class RolePo implements Serializable {
private String editUserId;
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField("edit_time")
private LocalDateTime editTime;
@TableField("tenant_id")
private String tenantId;

View File

@ -29,4 +29,12 @@ public interface RoleService extends IService<RolePo> {
* @date 2022/9/19 下午6:34
*/
List<RoleVo> findByIds(List<String> ids);
/**
* 查询当前租户下所有角色
*
* @return
*/
List<RoleVo> findList();
}

View File

@ -7,13 +7,16 @@ import cn.zyjblogs.server.role.service.RoleService;
import cn.zyjblogs.server.role.vo.RoleVo;
import cn.zyjblogs.starter.common.entity.context.BaseContext;
import cn.zyjblogs.starter.common.utils.bean.BeanUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ -55,4 +58,23 @@ public class RoleServiceImpl extends ServiceImpl<RoleMapper, RolePo> implements
}
return BeanUtils.map(rolePos, RoleVo.class);
}
/**
* 查询当前租户下所有角色
*
* @param
* @return java.util.List<cn.zyjblogs.server.role.vo.RoleVo>
* @author zhuyijun
* @date 2022/9/22 下午11:32
*/
@Override
public List<RoleVo> findList() {
LambdaQueryWrapper<RolePo> wrapper = Wrappers.lambdaQuery();
wrapper.eq(RolePo::getTenantId, BaseContext.getTenantId());
List<RolePo> rolePos = roleMapper.selectList(wrapper);
if (CollectionUtils.isEmpty(rolePos)) {
return new ArrayList<>();
}
return BeanUtils.map(rolePos, RoleVo.class);
}
}

View File

@ -1,5 +1,6 @@
package cn.zyjblogs.server.user.controller;
import cn.zyjblogs.server.user.dto.UserDto;
import cn.zyjblogs.server.user.dto.UserPageDto;
import cn.zyjblogs.server.user.po.UserPo;
import cn.zyjblogs.server.user.service.UserService;
@ -7,10 +8,15 @@ import cn.zyjblogs.server.user.vo.UserVo;
import cn.zyjblogs.starter.common.entity.response.ResponseObject;
import cn.zyjblogs.starter.common.entity.response.ResponseResult;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@ -21,6 +27,7 @@ import java.util.List;
/**
* @author zhuyijun
*/
@Api(tags = {"用户管理"})
@RestController
@RequestMapping("/user")
@RequiredArgsConstructor
@ -38,6 +45,7 @@ public class UserController {
* @author zhuyijun
* @date 2022/9/19 上午12:06
*/
@ApiOperation(value = "", notes = "通过id查询用户")
@GetMapping("/id")
public ResponseObject<UserPo> findById(@RequestParam String id) {
return ResponseResult.success(userService.getById(id));
@ -50,6 +58,7 @@ public class UserController {
* @author zhuyijun
* @date 2022/9/19 上午1:14
*/
@ApiOperation(value = "", notes = "分页查询")
@GetMapping("/page")
public ResponseObject<List<UserVo>> findPage(UserPageDto userPageDto) {
IPage<UserVo> page = userService.findPage(userPageDto);
@ -64,8 +73,15 @@ public class UserController {
* @author zhuyijun
* @date 2022/9/19 上午1:14
*/
@ApiOperation(value = "", notes = "通过id集合查询用户")
@PostMapping("/findByIds")
public ResponseObject<List<UserVo>> findByIds(List<String> ids) {
return ResponseResult.success(userService.findByIds(ids));
}
@ApiOperation(value = "", notes = "用户保存")
@PutMapping("/saveUser")
public ResponseObject<String> saveUser(@RequestBody @Validated UserDto userDto) {
return userService.saveUser(userDto);
}
}

View File

@ -0,0 +1,72 @@
package cn.zyjblogs.server.user.dto;
import cn.zyjblogs.starter.common.entity.constant.CommonConstant;
import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import io.swagger.annotations.ApiModel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotBlank;
import java.time.LocalDateTime;
/**
* @author zhuyijun
*/
@ApiModel(description = "用户Dto")
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class UserDto {
private String id;
@NotBlank(message = "账号不能为空")
private String username;
private String name;
private Integer age;
private String avatar;
private String phone;
private String email;
private String inviteUserId;
private Integer status;
private Integer followNum;
private Integer fansNum;
private Integer deleted;
private String description;
private String createUserId;
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JSONField(format = CommonConstant.DATETIME_PATTERN)
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = CommonConstant.DATETIME_PATTERN)
private LocalDateTime createTime;
private String editUserId;
@JSONField(format = CommonConstant.DATETIME_PATTERN)
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = CommonConstant.DATETIME_PATTERN)
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
private LocalDateTime editTime;
private String tenantId;
}

View File

@ -1,5 +1,6 @@
package cn.zyjblogs.server.user.mapper;
import cn.zyjblogs.server.user.dto.UserDto;
import cn.zyjblogs.server.user.dto.UserPageDto;
import cn.zyjblogs.server.user.po.UserPo;
import cn.zyjblogs.server.user.vo.UserVo;
@ -31,4 +32,12 @@ public interface UserMapper extends BaseMapper<UserPo> {
* @date 2022/9/19 上午12:47
*/
IPage<UserVo> findPage(IPage<UserPageDto> page, @Param("userPageDto") UserPageDto userPageDto, @Param("tenantId") String tenantId);
/**
* @param userDto
* @return java.lang.Integer
* @author zhuyijun
* @date 2022/9/22 下午6:11
*/
Integer checkUserCount(@Param("userDto") UserDto userDto);
}

View File

@ -1,12 +1,13 @@
package cn.zyjblogs.server.user.po;
import com.alibaba.fastjson.annotation.JSONField;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import lombok.AllArgsConstructor;
import lombok.Builder;
@ -14,7 +15,6 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.sql.Timestamp;
import java.time.LocalDateTime;
/**
@ -73,6 +73,7 @@ public class UserPo implements Serializable {
private String createUserId;
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField("create_time")
private LocalDateTime createTime;
@ -80,8 +81,8 @@ public class UserPo implements Serializable {
@TableField("edit_user_id")
private String editUserId;
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField("edit_time")
private LocalDateTime editTime;

View File

@ -1,10 +1,13 @@
package cn.zyjblogs.server.user.service;
import cn.zyjblogs.server.user.dto.UserDto;
import cn.zyjblogs.server.user.dto.UserPageDto;
import cn.zyjblogs.server.user.po.UserPo;
import cn.zyjblogs.server.user.vo.UserVo;
import cn.zyjblogs.starter.common.entity.response.ResponseObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.List;
@ -30,4 +33,12 @@ public interface UserService extends IService<UserPo> {
*/
List<UserVo> findByIds(List<String> ids);
/**
* @param userDto
* @return java.lang.String
* @author zhuyijun
* @date 2022/9/22 下午5:34
*/
ResponseObject<String> saveUser(@RequestBody UserDto userDto);
}

View File

@ -1,12 +1,16 @@
package cn.zyjblogs.server.user.service.impl;
import cn.zyjblogs.server.user.dto.UserDto;
import cn.zyjblogs.server.user.dto.UserPageDto;
import cn.zyjblogs.server.user.mapper.UserMapper;
import cn.zyjblogs.server.user.po.UserPo;
import cn.zyjblogs.server.user.service.UserService;
import cn.zyjblogs.server.user.vo.UserVo;
import cn.zyjblogs.starter.common.entity.context.BaseContext;
import cn.zyjblogs.starter.common.entity.response.HttpCode;
import cn.zyjblogs.starter.common.entity.response.ResponseObject;
import cn.zyjblogs.starter.common.entity.response.ResponseResult;
import cn.zyjblogs.starter.common.utils.bean.BeanUtils;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@ -56,4 +60,33 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, UserPo> implements
}
return BeanUtils.map(userPos, UserVo.class);
}
/**
* @param userDto
* @return java.lang.Boolean
* @author zhuyijun
* @date 2022/9/22 下午6:04
*/
@Override
public ResponseObject<String> saveUser(UserDto userDto) {
if (checkUser(userDto)) {
return ResponseResult.error(HttpCode.BAD_REQUEST, "该账号已存在");
}
UserPo userPo = BeanUtils.map(userDto, UserPo.class);
int count = userMapper.insert(userPo);
if (count > 0) {
return ResponseResult.success("用户创建成功");
}
return ResponseResult.error(HttpCode.BAD_REQUEST, "用户创建是失败");
}
/**
* 校验用户是否存在
*
* @param userDto
* @return
*/
public boolean checkUser(UserDto userDto) {
return userMapper.checkUserCount(userDto) > 0;
}
}

View File

@ -1,7 +1,7 @@
package cn.zyjblogs.server.user.vo;
import cn.zyjblogs.starter.common.entity.constant.CommonConstant;
import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import lombok.AllArgsConstructor;
@ -10,7 +10,6 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.sql.Timestamp;
import java.time.LocalDateTime;
/**
@ -51,14 +50,14 @@ public class UserVo implements Serializable {
private String createUserId;
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JSONField(format = CommonConstant.DATETIME_PATTERN)
private LocalDateTime createTime;
private String editUserId;
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JSONField(format = CommonConstant.DATETIME_PATTERN)
private LocalDateTime editTime;
private String tenantId;

View File

@ -3,7 +3,10 @@ spring:
active: test
---
spring:
main:
allow-bean-definition-overriding: true
application:
name: zyjblogs-rbac
cloud:
@ -18,6 +21,7 @@ spring:
shared-configs[0]:
data-id: zyjblogs-global-${spring.profiles.active}.yml
group: global
refresh: true
discovery:
server-addr: ${spring.cloud.nacos.config.server-addr}
username: ${spring.cloud.nacos.config.username}
@ -33,20 +37,3 @@ security:
client:
client-id: ${spring.application.name}
client-secret: secret
# 配置了公钥和私钥的位置
rsa:
key:
enable: true
pubKeyPath: public.txt
priKeyPath: private.txt
#security:
# oauth2:
# client:
# client-id: ${spring.application.name}
# client-secret: secret
# access-token-uri: http://localhost:9029/oauth/token
# grant-type: client_credentials
# scope: write read
# authorization:
# check-token-access: http://localhost:9029/oauth/check_token

View File

@ -1,8 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.zyjblogs.server.user.mapper.UserMapper">
<resultMap id="userMap" type="cn.zyjblogs.server.user.vo.UserVo">
<id column="id" property="id" javaType="java.lang.String"/>
<result column="username" property="username" javaType="java.lang.String"/>
<result column="username" property="username" javaType="java.lang.String"/>
<result column="name" property="name" javaType="java.lang.String"/>
<result column="username" property="username" javaType="java.lang.String"/>
<result column="age" property="age" javaType="java.lang.Integer"/>
<result column="avatar" property="avatar" javaType="java.lang.String"/>
<result column="phone" property="phone" javaType="java.lang.String"/>
<result column="email" property="email" javaType="java.lang.String"/>
<result column="invite_user_id" property="inviteUserId" javaType="java.lang.String"/>
<result column="status" property="status" javaType="java.lang.Integer"/>
<result column="follow_num" property="followNum" javaType="java.lang.Integer"/>
<result column="fans_num" property="fansNum" javaType="java.lang.Integer"/>
<result column="deleted" property="deleted" javaType="java.lang.Integer"/>
<result column="description" property="description" javaType="java.lang.String"/>
<result column="create_user_id" property="createUserId" javaType="java.lang.String"/>
<result column="create_time" property="createTime" javaType="java.sql.Timestamp"/>
<result column="edit_user_id" property="editUserId" javaType="java.lang.String"/>
<result column="edit_time" property="editTime" javaType="java.sql.Timestamp"/>
<result column="tenant_id" property="tenantId" javaType="java.lang.String"/>
</resultMap>
<sql id="base_user_sql">
id,username,name,age,avatar,phone,
email,invite_user_id,status,follow_num,
fans_num,deleted,description,create_user_id,
create_time,edit_user_id,edit_time,tenant_id
</sql>
<select id="findUserByname" resultType="cn.zyjblogs.server.user.po.UserPo">
select *
select
<include refid="base_user_sql"/>
from users
where deleted = 0
and username like CONCAT('%', #{username}, '%')
@ -11,19 +40,21 @@
</if>
</select>
<select id="findPage" resultType="cn.zyjblogs.server.user.vo.UserVo">
select * from users
select
<include refid="base_user_sql"/>
from users
where deleted = 0
<if test="userPageDto.username != null and userPageDto.username!='' ">
and username concat('%',#{userPageDto.username},'%')
and username like concat('%',#{userPageDto.username},'%')
</if>
<if test="userPageDto.name != null and userPageDto.name!='' ">
and name concat('%',#{name},'%')
and name like concat('%',#{name},'%')
</if>
<if test="userPageDto.phone != null and userPageDto.phone!='' ">
and phone concat('%',#{phone},'%')
and phone like concat('%',#{phone},'%')
</if>
<if test="userPageDto.email != null and userPageDto.email!='' ">
and email concat('%',#{email},'%')
and email like concat('%',#{email},'%')
</if>
<if test="userPageDto.email != null ">
and status = #{userPageDto.status}
@ -33,5 +64,23 @@
</if>
order by create_time desc
</select>
<select id="checkUserCount" resultType="java.lang.Integer">
select count(1)
from users
<where>
and deleted = 0 and username = #{userDto.username}
<if test="userDto.name != null and userDto.name!='' ">
or name = #{userDto.name}
</if>
<if test="userDto.phone != null and userDto.phone!='' ">
or phone = #{userDto.phone}
</if>
<if test="userDto.email != null and userDto.email!='' ">
or email = #{userDto.email}
</if>
<if test="userDto.tenantId != null and userDto.tenantId != '' ">
and tenant_id = #{userDto.tenantId}
</if>
</where>
</select>
</mapper>

View File

@ -11,19 +11,21 @@
</if>
</select>
<select id="findPage" resultType="cn.zyjblogs.server.user.vo.UserVo">
select * from users
select
<include refid="base_user_sql"/>
from users
where deleted = 0
<if test="userPageDto.username != null and userPageDto.username!='' ">
and username concat('%',#{userPageDto.username},'%')
and username like concat('%',#{userPageDto.username},'%')
</if>
<if test="userPageDto.name != null and userPageDto.name!='' ">
and name concat('%',#{name},'%')
and name like concat('%',#{name},'%')
</if>
<if test="userPageDto.phone != null and userPageDto.phone!='' ">
and phone concat('%',#{phone},'%')
and phone like concat('%',#{phone},'%')
</if>
<if test="userPageDto.email != null and userPageDto.email!='' ">
and email concat('%',#{email},'%')
and email like concat('%',#{email},'%')
</if>
<if test="userPageDto.email != null ">
and status = #{userPageDto.status}
@ -33,5 +35,23 @@
</if>
order by create_time desc
</select>
<select id="checkUserCount" resultType="java.lang.Integer">
select count(1)
from users
<where>
and deleted = 0 and username = #{userDto.username}
<if test="userDto.name != null and userDto.name!='' ">
or name = #{userDto.name}
</if>
<if test="userDto.phone != null and userDto.phone!='' ">
or phone = #{userDto.phone}
</if>
<if test="userDto.email != null and userDto.email!='' ">
or email = #{userDto.email}
</if>
<if test="userDto.tenantId != null and userDto.tenantId != '' ">
and tenant_id = #{userDto.tenantId}
</if>
</where>
</select>
</mapper>

View File

@ -25,11 +25,6 @@ public class ResponseObject<T> {
example = "200"
)
private int code;
@ApiModelProperty(
value = "错误码",
example = "user_not_exists"
)
private String errorCode;
@ApiModelProperty(
value = "响应结果信息",
example = "请求成功"

View File

@ -1,6 +1,20 @@
package cn.zyjblogs.starter.common.utils.jwt;
import io.jsonwebtoken.*;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Clock;
import io.jsonwebtoken.CompressionCodec;
import io.jsonwebtoken.CompressionCodecResolver;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Header;
import io.jsonwebtoken.JwsHeader;
import io.jsonwebtoken.Jwt;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.PrematureJwtException;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.SignatureException;
import io.jsonwebtoken.SigningKeyResolver;
import io.jsonwebtoken.UnsupportedJwtException;
import io.jsonwebtoken.impl.DefaultClaims;
import io.jsonwebtoken.impl.DefaultClock;
import io.jsonwebtoken.impl.DefaultHeader;
@ -21,8 +35,9 @@ import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
/**
* jwt解析
*
* @author zhuyijun
*/
public class JwtParsers extends DefaultJwtParser {

View File

@ -1,25 +1,37 @@
package cn.zyjblogs.starter.common.utils.rsa;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import javax.crypto.Cipher;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.stream.Collectors;
/**
* Copyright (C), 2021, 北京同创永益科技发展有限公司
*
* @author meiji
* @version 3.0.0
* @description RsaUtils
* @date 2021/11/4 9:18
*/
@Slf4j
public final class RsaUtils {
public class RsaUtils {
private static final String DEFAULT_ALGORITHM = "RSA";
private static final Logger log = LoggerFactory.getLogger(RsaUtils.class);
private RsaUtils() {
}
@ -86,7 +98,6 @@ public class RsaUtils {
* @param rawStr
* @param privateKey
* @return java.lang.String
* @author meiji
* @date 2021/11/4 9:43
*/
public static String privateKeyEncrypt(String rawStr, String privateKey) throws Exception {
@ -103,7 +114,6 @@ public class RsaUtils {
* @param encodeStr
* @param privateKey
* @return java.lang.String
* @author meiji
* @date 2021/11/4 9:43
*/
public static String privateKeyDecrypt(String encodeStr, String privateKey) throws Exception {
@ -119,29 +129,25 @@ public class RsaUtils {
*
* @param path 公钥路径
* @return 公钥
* @author tanyuanzhi
* @date 2021/11/9 10:06
*/
public static PublicKey getPublicKeyByPath(String path) {
InputStream inputStream = RsaUtils.class.getClassLoader().getResourceAsStream(path);
if (inputStream == null) {
log.error("获取公钥出错,找不到公钥文件");
return null;
}
String publicKeyStr = new BufferedReader(new InputStreamReader(inputStream)).lines().collect(Collectors.joining());
publicKeyStr = publicKeyStr.replace("-----BEGIN PUBLIC KEY-----", "").replace("-----END PUBLIC KEY-----", "");
byte[] keyBytes = Base64.getMimeDecoder().decode(publicKeyStr);
try {
String key = IOUtils.toString(Paths.get(path).toUri(), StandardCharsets.UTF_8);
key = key.replace("-----BEGIN PUBLIC KEY-----", "").replace("-----END PUBLIC KEY-----", "");
byte[] keyBytes = Base64.getMimeDecoder().decode(key);
// 通过X509编码的Key指令获得公钥对象
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
try {
KeyFactory keyFactory = KeyFactory.getInstance(DEFAULT_ALGORITHM);
PublicKey publicKey = keyFactory.generatePublic(keySpec);
return publicKey;
} catch (Exception e) {
} catch (IOException | InvalidKeySpecException e) {
log.error("获取公钥出错", e);
return null;
} catch (NoSuchAlgorithmException e) {
log.error("不支持此算法", e);
return null;
}
}
}

View File

@ -1,35 +0,0 @@
package cn.zyjblogs.starter.common.utils.web;
import cn.zyjblogs.starter.common.entity.constant.HttpHeaderConstant;
import org.apache.commons.lang3.StringUtils;
import javax.servlet.http.HttpServletRequest;
public class RequestUtils {
private RequestUtils() {
}
/**
* 请求是否来自网关
*
* @param request 请求信息
* @return boolean
* @date 2022/3/3 14:19
*/
public static boolean requestFromGateway(HttpServletRequest request) {
return StringUtils.isNotEmpty(request.getHeader(HttpHeaderConstant.REQUEST_FROM_GATEWAY_KEY));
}
/**
* 请求是否来自feign调用
*
* @param request 请求信息
* @return boolean
* @date 2022/3/3 14:19
*/
public static boolean requestFromFeign(HttpServletRequest request) {
return StringUtils.isNotEmpty(request.getHeader(HttpHeaderConstant.REQUEST_FROM_FEIGN_KEY));
}
}

View File

@ -46,6 +46,20 @@
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-typehandlers-jsr310</artifactId>
<version>1.0.2</version>
</dependency>
</dependencies>
<build>
<plugins>

View File

@ -0,0 +1,63 @@
package cn.zyjblogs.starter.mybatisplus.autoconfigure;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* 定义转换器支持的JAVA类型
* 定义转换器支持的数据库类型
*
* @author zhuyijun
*/
@Component
@MappedTypes(LocalDateTime.class)
@MappedJdbcTypes(value = JdbcType.TIMESTAMP, includeNullJdbcType = true)
public class CustomLocalDateTimeTypeHandler extends BaseTypeHandler<LocalDateTime> {
private final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Override
public void setNonNullParameter(PreparedStatement ps, int i, LocalDateTime parameter, JdbcType jdbcType)
throws SQLException {
if (parameter != null) {
ps.setString(i, dateTimeFormatter.format(parameter));
}
}
@Override
public LocalDateTime getNullableResult(ResultSet rs, String columnName) throws SQLException {
String target = rs.getString(columnName);
if (StringUtils.isEmpty(target)) {
return null;
}
return LocalDateTime.parse(target, dateTimeFormatter);
}
@Override
public LocalDateTime getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
String target = rs.getString(columnIndex);
if (StringUtils.isEmpty(target)) {
return null;
}
return LocalDateTime.parse(target, dateTimeFormatter);
}
@Override
public LocalDateTime getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
String target = cs.getString(columnIndex);
if (StringUtils.isEmpty(target)) {
return null;
}
return LocalDateTime.parse(target, dateTimeFormatter);
}
}

View File

@ -0,0 +1,37 @@
package cn.zyjblogs.starter.mybatisplus.autoconfigure;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.autoconfigure.jdbc.DataSourceHealthContributorAutoConfiguration;
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator;
import org.springframework.boot.jdbc.metadata.DataSourcePoolMetadataProvider;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
import javax.sql.DataSource;
import java.util.Map;
/**
* @author zhuyijun
*/
@Configuration
public class DataSourceHealthConfig extends DataSourceHealthContributorAutoConfiguration {
@Value("${spring.datasource.dbcp2.validation-query:select 1}")
private String defaultQuery;
public DataSourceHealthConfig(Map<String, DataSource> dataSources, ObjectProvider<DataSourcePoolMetadataProvider> metadataProviders) {
super(dataSources, metadataProviders);
}
@Override
protected AbstractHealthIndicator createIndicator(DataSource source) {
DataSourceHealthIndicator indicator = (DataSourceHealthIndicator) super.createIndicator(source);
if (!StringUtils.hasText(indicator.getQuery())) {
indicator.setQuery(defaultQuery);
}
return indicator;
}
}

View File

@ -1,2 +1,4 @@
org.springframework.boot.cn.zyjblogs.starter.feign.autoconfigure.EnableAutoConfiguration=\
cn.zyjblogs.starter.mybatisplus.autoconfigure.MyBatisPlusConfig
cn.zyjblogs.starter.mybatisplus.autoconfigure.MyBatisPlusConfig,\
cn.zyjblogs.starter.mybatisplus.autoconfigure.DataSourceHealthConfig,\
cn.zyjblogs.starter.mybatisplus.autoconfigure.CustomLocalDateTimeTypeHandler

View File

@ -7,6 +7,6 @@ import org.springframework.context.annotation.Configuration;
* @author zhuyijun
*/
@Configuration
@EnableConfigurationProperties(RsaKeyProperties.class)
@EnableConfigurationProperties({RsaKeyProperties.class, WhiteListProperties.class})
public class OauthAutoConfiguration {
}

View File

@ -4,6 +4,7 @@ package cn.zyjblogs.starter.oauth.autoconfigure;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
/**
* rsa 路径配置文件
@ -13,6 +14,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
@Getter
@Setter
@ConfigurationProperties(prefix = "rsa.key")
@RefreshScope
public class RsaKeyProperties {
/**
* 启用rsa对token进行加密

View File

@ -0,0 +1,20 @@
package cn.zyjblogs.starter.oauth.autoconfigure;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import java.util.List;
/**
* 白名单
*
* @author zhuyijun
*/
@Data
@RefreshScope
@ConfigurationProperties(prefix = "zyjblogs.whilelist")
public class WhiteListProperties {
private List<String> allowPaths;
}

View File

@ -1,4 +1,4 @@
package cn.zyjblogs.starter.oauth.config;
package cn.zyjblogs.starter.oauth.interceptor;
import cn.zyjblogs.starter.common.entity.constant.HttpHeaderConstant;
import cn.zyjblogs.starter.common.entity.context.BaseContext;
@ -10,10 +10,13 @@ import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author zhuyijun
*/
@Slf4j
public class JwtTokenHandlerInterceptor extends HandlerInterceptorAdapter {
private boolean checkInner;
private final boolean checkInner;
public JwtTokenHandlerInterceptor(boolean checkInner) {
this.checkInner = checkInner;
@ -22,6 +25,9 @@ public class JwtTokenHandlerInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String authorization = request.getHeader(HttpHeaderConstant.AUTHORIZATION);
if (!checkInner) {
return true;
}
//token为空直接放行
if (StringUtils.isEmpty(authorization)) {
BaseContext.set(ContextDto.builder().build());

View File

@ -1,4 +1,4 @@
package cn.zyjblogs.starter.oauth.config;
package cn.zyjblogs.starter.oauth.interceptor;
import cn.zyjblogs.starter.common.entity.constant.HttpHeaderConstant;
import cn.zyjblogs.starter.common.entity.context.BaseContext;
@ -29,12 +29,13 @@ public class OauthFeignInterceptorAutoConfiguration {
/**
* token信息补全,token不能为空
*
* @return 补全后得token
* @author liuweicheng
* @date 2022/03/12
* @return 补全后得token
*/
private String tokenCompletion(String token){
if(token.indexOf(HttpHeaderConstant.AUTHORIZATION_TYPE) != 0){
private String tokenCompletion(String token) {
if (token.indexOf(HttpHeaderConstant.AUTHORIZATION_TYPE) != 0) {
token = HttpHeaderConstant.AUTHORIZATION_TYPE + " " + token;
}
return token;

View File

@ -1,4 +1,4 @@
package cn.zyjblogs.starter.oauth.config;
package cn.zyjblogs.starter.oauth.interceptor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;

View File

@ -1,5 +1,6 @@
package cn.zyjblogs.starter.oauth.resource;
import cn.zyjblogs.starter.oauth.autoconfigure.WhiteListProperties;
import cn.zyjblogs.starter.oauth.handler.ResourceAccessDeniedHandler;
import cn.zyjblogs.starter.oauth.handler.ResourceAuthenticationEntryPoint;
import lombok.RequiredArgsConstructor;
@ -26,6 +27,7 @@ public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Value("${spring.application.name}")
private String resourceId;
private final TokenStore tokenStore;
private final WhiteListProperties whiteListProperties;
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
@ -41,6 +43,7 @@ public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/webjars/**", "/swagger-ui.html/**", "/swagger-resources/**", "/v2/api-docs/**", String.join(",", whiteListProperties.getAllowPaths())).permitAll()
.anyRequest()
.authenticated()
.and()

View File

@ -1,4 +1,4 @@
package cn.zyjblogs.starter.oauth.security;
package cn.zyjblogs.starter.oauth.token;
import cn.zyjblogs.starter.common.entity.constant.ContextKeyConstant;
import cn.zyjblogs.starter.common.entity.context.BaseContext;

View File

@ -1,10 +1,12 @@
package cn.zyjblogs.starter.oauth.security;
package cn.zyjblogs.starter.oauth.token;
import cn.zyjblogs.starter.oauth.autoconfigure.RsaKeyProperties;
import lombok.RequiredArgsConstructor;
import org.apache.commons.io.IOUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
@ -23,6 +25,11 @@ public class TokenConfig {
private final RsaKeyProperties rsaKeyProperties;
private final OauthAccessTokenConverter oauthAccessTokenConverter;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 令牌存储策略
*

View File

@ -1,7 +1,7 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
cn.zyjblogs.starter.oauth.config.OauthFeignInterceptorAutoConfiguration,\
cn.zyjblogs.starter.oauth.interceptor.OauthFeignInterceptorAutoConfiguration,\
cn.zyjblogs.starter.oauth.resource.ResourceServerConfig,\
cn.zyjblogs.starter.oauth.security.TokenConfig,\
cn.zyjblogs.starter.oauth.security.OauthAccessTokenConverter,\
cn.zyjblogs.starter.oauth.config.OauthInterceptorAutoConfiguration,\
cn.zyjblogs.starter.oauth.token.TokenConfig,\
cn.zyjblogs.starter.oauth.token.OauthAccessTokenConverter,\
cn.zyjblogs.starter.oauth.interceptor.OauthInterceptorAutoConfiguration,\
cn.zyjblogs.starter.oauth.autoconfigure.OauthAutoConfiguration

View File

@ -45,6 +45,12 @@
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<!-- Spring 集成 -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
</dependency>
</dependencies>
<build>
<plugins>

View File

@ -0,0 +1,145 @@
package cn.zyjblogs.starter.web.autoconfig;
import com.google.common.collect.Lists;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;
import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.Contact;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.service.SecurityScheme;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider;
import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider;
import java.lang.reflect.Field;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZonedDateTime;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
@EnableOpenApi
@Configuration
@Import(BeanValidatorPluginsConfiguration.class)
public class Knife4jAutoConfigurationConfig {
@Value("${spring.application.name}")
private String applicationName;
// @Bean
// public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() {
// return new BeanPostProcessor() {
//
// @Override
// public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {
// customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
// }
// return bean;
// }
//
// private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) {
// List<T> copy = mappings.stream()
// .filter(mapping -> mapping.getPatternParser() == null)
// .collect(Collectors.toList());
// mappings.clear();
// mappings.addAll(copy);
// }
//
// @SuppressWarnings("unchecked")
// private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) {
// try {
// Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");
// field.setAccessible(true);
// return (List<RequestMappingInfoHandlerMapping>) field.get(bean);
// } catch (IllegalArgumentException | IllegalAccessException e) {
// throw new IllegalStateException(e);
// }
// }
// };
// }
@Bean(value = "defaultApi")
public Docket defaultApi() {
return new Docket(DocumentationType.SWAGGER_2)
.directModelSubstitute(LocalDateTime.class, Date.class)
.directModelSubstitute(LocalDate.class, String.class)
.directModelSubstitute(LocalTime.class, String.class)
.directModelSubstitute(ZonedDateTime.class, String.class)
.apiInfo(apiInfo())
.select()
//controller层包路径
.apis(RequestHandlerSelectors.basePackage("cn.zyjblogs.server"))
.paths(PathSelectors.any())
.build()
.securityContexts(Lists.newArrayList(securityContext(), securityContext())).securitySchemes(Lists.<SecurityScheme>newArrayList(apiKey()));
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title(applicationName.toUpperCase() + " RESTFUL APIS")
.contact(new Contact("zyjblogs", "", ""))
.version("1.0.0")
.build();
}
/**
* token头key
*
* @param
* @return ApiKey
* @author liuweicheng
* @date 2021/11/4 11:29
*/
private ApiKey apiKey() {
return new ApiKey("BearerToken", "Authorization", "header");
}
/**
* token定义
*
* @param
* @return SecurityContext
* @author liuweicheng
* @date 2021/11/4 11:28
*/
private SecurityContext securityContext() {
return SecurityContext.builder()
.securityReferences(defaultAuth())
.forPaths(PathSelectors.regex("/.*"))
.build();
}
/**
* token描述定义
*
* @param
* @return java.util.List<springfox.documentation.service.SecurityReference>
* @author zhuyijun
* @date 2022/9/23 上午12:20
*/
private List<SecurityReference> defaultAuth() {
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
return Lists.newArrayList(new SecurityReference("BearerToken", authorizationScopes));
}
}

View File

@ -1,3 +1,4 @@
## Auto Configure
org.springframework.boot.cn.zyjblogs.starter.feign.autoconfigure.EnableAutoConfiguration=\
cn.zyjblogs.starter.web.autoconfig.GlobalExceptionHandlerAutoConfiguration
cn.zyjblogs.starter.web.autoconfig.GlobalExceptionHandlerAutoConfiguration,\
cn.zyjblogs.starter.web.autoconfig.Knife4jAutoConfigurationConfig