mirror of
https://github.com/luoye663/e5.git
synced 2024-12-26 03:38:53 +00:00
Merge remote-tracking branch 'origin/master'
# Conflicts: # src/main/java/io/qyi/e5/config/security/SecurityAuthenticationHandler.java # src/main/java/io/qyi/e5/config/security/SecurityConfig.java # src/main/java/io/qyi/e5/config/security/UsernamePasswordAuthenticationProvider.java # src/main/java/io/qyi/e5/config/security/filter/LinkTokenAuthenticationFilter.java
This commit is contained in:
commit
fe77f2d7f2
21
pom.xml
21
pom.xml
@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-parent</artifactId>
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
<version>2.2.4.RELEASE</version>
|
<version>2.3.1.RELEASE</version>
|
||||||
<relativePath/> <!-- lookup parent from repository -->
|
<relativePath/> <!-- lookup parent from repository -->
|
||||||
</parent>
|
</parent>
|
||||||
<groupId>io.qyi</groupId>
|
<groupId>io.qyi</groupId>
|
||||||
@ -35,16 +35,23 @@
|
|||||||
</exclusion>
|
</exclusion>
|
||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- log4j2 -->
|
<!--排除自带的logging-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter</artifactId>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-logging</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!--log4j2-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-log4j2</artifactId>
|
<artifactId>spring-boot-starter-log4j2</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- freemarker 模板引擎-->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-freemarker</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<!--权限管理插件-->
|
<!--权限管理插件-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
@ -0,0 +1,65 @@
|
|||||||
|
package io.qyi.e5.config.security;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.security.access.AccessDecisionManager;
|
||||||
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
|
import org.springframework.security.access.ConfigAttribute;
|
||||||
|
import org.springframework.security.authentication.InsufficientAuthenticationException;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 决策管理器
|
||||||
|
*
|
||||||
|
* @program: e5
|
||||||
|
* @description:
|
||||||
|
* @author: 落叶随风
|
||||||
|
* @create: 2020-06-15 16:11
|
||||||
|
**/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class MyAccessDecisionManager implements AccessDecisionManager {
|
||||||
|
@Override
|
||||||
|
public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException {
|
||||||
|
if (collection == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
System.out.println(o.toString()); // object is a URL.
|
||||||
|
log.info("object is a URL. {}", o.toString());
|
||||||
|
//所请求的资源拥有的权限(一个资源对多个权限)
|
||||||
|
Iterator<ConfigAttribute> iterator = collection.iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
ConfigAttribute configAttribute = iterator.next();
|
||||||
|
//访问所请求资源所需要的权限
|
||||||
|
String needPermission = configAttribute.getAttribute();
|
||||||
|
log.info("访问 " + o.toString() + " 需要的权限是:" + needPermission);
|
||||||
|
if (needPermission == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
//用户所拥有的权限authentication
|
||||||
|
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
|
||||||
|
for (GrantedAuthority ga : authorities) {
|
||||||
|
if (needPermission.equals(ga.getAuthority())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//没有权限
|
||||||
|
throw new AccessDeniedException(" 无权限! ");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports(ConfigAttribute configAttribute) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports(Class<?> aClass) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
package io.qyi.e5.config.security;
|
||||||
|
|
||||||
|
import org.springframework.security.access.ConfigAttribute;
|
||||||
|
import org.springframework.security.access.SecurityConfig;
|
||||||
|
import org.springframework.security.web.FilterInvocation;
|
||||||
|
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
|
||||||
|
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @program: e5
|
||||||
|
* @description:
|
||||||
|
* @author: 落叶随风
|
||||||
|
* @create: 2020-06-17 16:25
|
||||||
|
**/
|
||||||
|
@Service
|
||||||
|
public class MyInvocationSecurityMetadataSourceService implements FilterInvocationSecurityMetadataSource {
|
||||||
|
|
||||||
|
private HashMap<String, Collection<ConfigAttribute>> map =null;
|
||||||
|
/**
|
||||||
|
* 加载权限表中所有权限
|
||||||
|
*/
|
||||||
|
public void loadResourceDefine(){
|
||||||
|
map = new HashMap<>();
|
||||||
|
Collection<ConfigAttribute> array;
|
||||||
|
ConfigAttribute cfg;
|
||||||
|
List<Map<String, String>> permissions = new LinkedList<>();
|
||||||
|
for(Map<String, String> permission : permissions) {
|
||||||
|
array = new ArrayList<>();
|
||||||
|
cfg = new SecurityConfig("ADMIN");
|
||||||
|
//此处只添加了用户的名字,其实还可以添加更多权限的信息,例如请求方法到ConfigAttribute的集合中去。此处添加的信息将会作为MyAccessDecisionManager类的decide的第三个参数。
|
||||||
|
array.add(cfg);
|
||||||
|
//用权限的getUrl() 作为map的key,用ConfigAttribute的集合作为 value,
|
||||||
|
map.put("/admin/test", array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {
|
||||||
|
if(map ==null) loadResourceDefine();
|
||||||
|
//object 中包含用户请求的request 信息
|
||||||
|
HttpServletRequest request = ((FilterInvocation) o).getHttpRequest();
|
||||||
|
AntPathRequestMatcher matcher;
|
||||||
|
String resUrl;
|
||||||
|
for(Iterator<String> iter = map.keySet().iterator(); iter.hasNext(); ) {
|
||||||
|
resUrl = iter.next();
|
||||||
|
matcher = new AntPathRequestMatcher(resUrl);
|
||||||
|
if(matcher.matches(request)) {
|
||||||
|
return map.get(resUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<ConfigAttribute> getAllConfigAttributes() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports(Class<?> aClass) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -58,11 +58,12 @@ public class UsernamePasswordAuthenticationToken extends AbstractAuthenticationT
|
|||||||
|
|
||||||
|
|
||||||
// 创建已认证的用户密码认证对象
|
// 创建已认证的用户密码认证对象
|
||||||
public UsernamePasswordAuthenticationToken(String name, String avatar_url, int github_id, Collection<? extends GrantedAuthority> authorities) {
|
public UsernamePasswordAuthenticationToken(String name, String avatar_url, int github_id,String Authority, Collection<? extends GrantedAuthority> authorities) {
|
||||||
super(authorities);
|
super(authorities);
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.avatar_url = avatar_url;
|
this.avatar_url = avatar_url;
|
||||||
this.github_id = github_id;
|
this.github_id = github_id;
|
||||||
|
this.Authority = Authority;
|
||||||
super.setAuthenticated(true);
|
super.setAuthenticated(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package io.qyi.e5.controller;
|
package io.qyi.e5.controller.admin;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
@ -10,8 +10,12 @@ import org.springframework.amqp.core.MessageProperties;
|
|||||||
import org.springframework.amqp.rabbit.connection.CorrelationData;
|
import org.springframework.amqp.rabbit.connection.CorrelationData;
|
||||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.access.annotation.Secured;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
@ -22,8 +26,8 @@ import java.util.UUID;
|
|||||||
* @author: 落叶随风
|
* @author: 落叶随风
|
||||||
* @create: 2020-03-16 01:01
|
* @create: 2020-03-16 01:01
|
||||||
**/
|
**/
|
||||||
@Controller
|
|
||||||
@RestController
|
@RestController
|
||||||
|
@RequestMapping("/admin")
|
||||||
public class TestController {
|
public class TestController {
|
||||||
@Autowired
|
@Autowired
|
||||||
RabbitTemplate rabbitTemplate;
|
RabbitTemplate rabbitTemplate;
|
||||||
@ -47,5 +51,18 @@ public class TestController {
|
|||||||
return "ok";
|
return "ok";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/emptyRedis")
|
||||||
|
public String emptyRedis() {
|
||||||
|
redisUtil.deleteALL();
|
||||||
|
return "ok";
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/test")
|
||||||
|
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||||
|
public String test() {
|
||||||
|
return "ok";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -6,6 +6,7 @@ import io.qyi.e5.bean.result.ResultEnum;
|
|||||||
import io.qyi.e5.config.security.UsernamePasswordAuthenticationToken;
|
import io.qyi.e5.config.security.UsernamePasswordAuthenticationToken;
|
||||||
import io.qyi.e5.outlook.entity.Outlook;
|
import io.qyi.e5.outlook.entity.Outlook;
|
||||||
import io.qyi.e5.outlook.service.IOutlookService;
|
import io.qyi.e5.outlook.service.IOutlookService;
|
||||||
|
import io.qyi.e5.service.task.ITask;
|
||||||
import io.qyi.e5.util.EncryptUtil;
|
import io.qyi.e5.util.EncryptUtil;
|
||||||
import io.qyi.e5.util.ResultUtil;
|
import io.qyi.e5.util.ResultUtil;
|
||||||
import io.qyi.e5.util.redis.RedisUtil;
|
import io.qyi.e5.util.redis.RedisUtil;
|
||||||
@ -42,6 +43,9 @@ public class AuthController {
|
|||||||
@Value("${outlook.authorize.url}")
|
@Value("${outlook.authorize.url}")
|
||||||
String authorizeUrl;
|
String authorizeUrl;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
ITask Task;
|
||||||
|
|
||||||
@RequestMapping("/receive")
|
@RequestMapping("/receive")
|
||||||
public Result Receive(String code, String state, String session_state) throws Exception {
|
public Result Receive(String code, String state, String session_state) throws Exception {
|
||||||
if (!redisUtil.hasKey(states + state)) {
|
if (!redisUtil.hasKey(states + state)) {
|
||||||
@ -62,6 +66,8 @@ public class AuthController {
|
|||||||
if (!authorization_code) {
|
if (!authorization_code) {
|
||||||
return ResultUtil.error(-3, "clientId 或 clientSecret 填写错误!授权失败!");
|
return ResultUtil.error(-3, "clientId 或 clientSecret 填写错误!授权失败!");
|
||||||
}
|
}
|
||||||
|
/*添加此用户进消息队列*/
|
||||||
|
Task.sendTaskOutlookMQ(outlook.getGithubId());
|
||||||
return ResultUtil.success();
|
return ResultUtil.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,6 +206,9 @@ public class OutlookServiceImpl extends ServiceImpl<OutlookMapper, Outlook> impl
|
|||||||
count = jsonObject.get("value").getAsJsonArray().size();
|
count = jsonObject.get("value").getAsJsonArray().size();
|
||||||
}
|
}
|
||||||
JsonArray value = jsonObject.get("value").getAsJsonArray();
|
JsonArray value = jsonObject.get("value").getAsJsonArray();
|
||||||
|
if (count == 0) {
|
||||||
|
count = value.size();
|
||||||
|
}
|
||||||
for (int i = 0; i < count - 1; i++) {
|
for (int i = 0; i < count - 1; i++) {
|
||||||
JsonObject mail = value.get(i).getAsJsonObject();
|
JsonObject mail = value.get(i).getAsJsonObject();
|
||||||
String id = mail.get("id").getAsString();
|
String id = mail.get("id").getAsString();
|
||||||
|
@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|||||||
import io.qyi.e5.outlook.entity.Outlook;
|
import io.qyi.e5.outlook.entity.Outlook;
|
||||||
import io.qyi.e5.outlook.service.IOutlookService;
|
import io.qyi.e5.outlook.service.IOutlookService;
|
||||||
import io.qyi.e5.service.task.ITask;
|
import io.qyi.e5.service.task.ITask;
|
||||||
|
import io.qyi.e5.util.redis.RedisUtil;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.amqp.core.MessageProperties;
|
import org.springframework.amqp.core.MessageProperties;
|
||||||
@ -33,6 +34,9 @@ public class TaskImpl implements ITask {
|
|||||||
@Autowired
|
@Autowired
|
||||||
RabbitTemplate rabbitTemplate;
|
RabbitTemplate rabbitTemplate;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
RedisUtil redisUtil;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Async
|
@Async
|
||||||
public void sendTaskOutlookMQ(int github_id) {
|
public void sendTaskOutlookMQ(int github_id) {
|
||||||
@ -42,9 +46,12 @@ public class TaskImpl implements ITask {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/*根据用户设置生成随机数*/
|
/*根据用户设置生成随机数*/
|
||||||
String Expiration = getRandom(Outlook.getCronTimeRandomStart(), Outlook.getCronTimeRandomEnd());
|
int Expiration = getRandom(Outlook.getCronTimeRandomStart(), Outlook.getCronTimeRandomEnd());
|
||||||
send(github_id, Expiration);
|
/*将此用户信息加入redis,如果存在则代表在队列中,同时提前10秒过期*/
|
||||||
|
if (!redisUtil.hasKey("user.mq:" + github_id)) {
|
||||||
|
redisUtil.set("user.mq:" + github_id, 0, Expiration - 10);
|
||||||
|
send(github_id, Expiration* 1000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -55,14 +62,16 @@ public class TaskImpl implements ITask {
|
|||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
Outlook next = iterator.next();
|
Outlook next = iterator.next();
|
||||||
/*根据用户设置生成随机数*/
|
/*根据用户设置生成随机数*/
|
||||||
String Expiration = getRandom(next.getCronTimeRandomStart(), next.getCronTimeRandomEnd());
|
int Expiration = getRandom(next.getCronTimeRandomStart(), next.getCronTimeRandomEnd());
|
||||||
// System.out.println("生成随机调用时间,github ID" + next.getGithubId() + ",时间:" + Expiration);
|
/*将此用户信息加入redis,如果存在则代表在队列中,同时提前10秒过期*/
|
||||||
send(next.getGithubId(), Expiration);
|
if (!redisUtil.hasKey("user.mq:" + next.getGithubId())) {
|
||||||
|
redisUtil.set("user.mq:" + next.getGithubId(), 0, Expiration - 10);
|
||||||
|
send(next.getGithubId(), Expiration * 1000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Async
|
|
||||||
public void executeE5(int github_id) {
|
public void executeE5(int github_id) {
|
||||||
Outlook Outlook = outlookService.getOne(new QueryWrapper<Outlook>().eq("github_id", github_id));
|
Outlook Outlook = outlookService.getOne(new QueryWrapper<Outlook>().eq("github_id", github_id));
|
||||||
if (Outlook == null) {
|
if (Outlook == null) {
|
||||||
@ -82,7 +91,7 @@ public class TaskImpl implements ITask {
|
|||||||
* @Author: 落叶随风
|
* @Author: 落叶随风
|
||||||
* @Date: 2020/4/16
|
* @Date: 2020/4/16
|
||||||
*/
|
*/
|
||||||
public void send(Object msg, String Expiration) {
|
public void send(Object msg, int Expiration) {
|
||||||
CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
|
CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
|
||||||
|
|
||||||
rabbitTemplate.convertAndSend("delay", "delay", msg, message -> {
|
rabbitTemplate.convertAndSend("delay", "delay", msg, message -> {
|
||||||
@ -90,7 +99,7 @@ public class TaskImpl implements ITask {
|
|||||||
// 设置这条消息的过期时间
|
// 设置这条消息的过期时间
|
||||||
// messageProperties.setExpiration(Expiration);
|
// messageProperties.setExpiration(Expiration);
|
||||||
|
|
||||||
messageProperties.setHeader("x-delay",Expiration);
|
messageProperties.setHeader("x-delay", Expiration);
|
||||||
return message;
|
return message;
|
||||||
}, correlationData);
|
}, correlationData);
|
||||||
}
|
}
|
||||||
@ -105,9 +114,9 @@ public class TaskImpl implements ITask {
|
|||||||
* @Author: 落叶随风
|
* @Author: 落叶随风
|
||||||
* @Date: 2020/4/16
|
* @Date: 2020/4/16
|
||||||
*/
|
*/
|
||||||
public String getRandom(int start, int end) {
|
public int getRandom(int start, int end) {
|
||||||
Random r = new Random();
|
Random r = new Random();
|
||||||
String Expiration = String.valueOf((r.nextInt(end - start + 1) + start) * 1000);
|
int Expiration = (r.nextInt(end - start + 1) + start);
|
||||||
return Expiration;
|
return Expiration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,19 @@ public class RedisUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除所有键值对
|
||||||
|
* @title deleteALl
|
||||||
|
* @description
|
||||||
|
* @author 落叶随风
|
||||||
|
* @updateTime 2020/4/22 22:53
|
||||||
|
* @throws
|
||||||
|
*/
|
||||||
|
public void deleteALL() {
|
||||||
|
Set<String> keys = redisTemplate.keys("*");
|
||||||
|
redisTemplate.delete(keys);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据key 获取过期时间
|
* 根据key 获取过期时间
|
||||||
*
|
*
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,
|
<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,
|
||||||
当设置成trace时,会看到log4j2内部各种详细输出-->
|
当设置成trace时,会看到log4j2内部各种详细输出-->
|
||||||
<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数-->
|
<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数-->
|
||||||
<configuration status="DEBUG" monitorInterval="5">
|
<configuration status="INFO" monitorInterval="5">
|
||||||
<Properties>
|
<Properties>
|
||||||
<!-- 日志模板 -->
|
<!-- 日志模板 -->
|
||||||
<Property name="log_pattern" value="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
|
<Property name="log_pattern" value="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
|
||||||
|
Loading…
Reference in New Issue
Block a user