mirror of
https://gitee.com/youlaitech/youlai-mall.git
synced 2024-12-23 13:03:43 +08:00
feat:会话强制下线
This commit is contained in:
parent
b01367fb2a
commit
112a8b1d19
9
pom.xml
9
pom.xml
@ -46,9 +46,10 @@
|
|||||||
<seata.version>1.4.1</seata.version>
|
<seata.version>1.4.1</seata.version>
|
||||||
<commons-pool2-version>2.9.0</commons-pool2-version>
|
<commons-pool2-version>2.9.0</commons-pool2-version>
|
||||||
<knife4j.version>2.0.8</knife4j.version>
|
<knife4j.version>2.0.8</knife4j.version>
|
||||||
|
<redisson.version>3.11.1</redisson.version>
|
||||||
<logstash-logback-encoder.version>6.6</logstash-logback-encoder.version>
|
<logstash-logback-encoder.version>6.6</logstash-logback-encoder.version>
|
||||||
<elasticsearch.version>7.10.1</elasticsearch.version>
|
<elasticsearch.version>7.10.1</elasticsearch.version>
|
||||||
<redisson.version>3.11.1</redisson.version>
|
<ip2region.version>1.7.2</ip2region.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
@ -174,6 +175,12 @@
|
|||||||
<version>${redisson.version}</version>
|
<version>${redisson.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.lionsoul</groupId>
|
||||||
|
<artifactId>ip2region</artifactId>
|
||||||
|
<version>${ip2region.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
package com.youlai.admin.pojo.domain;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hxr
|
||||||
|
* @date 2021-03-09
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class LoginRecord {
|
||||||
|
|
||||||
|
private String _id;
|
||||||
|
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
private String clientIP;
|
||||||
|
|
||||||
|
private long elapsedTime;
|
||||||
|
|
||||||
|
private Object message;
|
||||||
|
|
||||||
|
private String token;
|
||||||
|
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
private String loginTime;
|
||||||
|
|
||||||
|
private String region;
|
||||||
|
|
||||||
|
}
|
@ -77,7 +77,10 @@
|
|||||||
<artifactId>spring-boot-starter-web</artifactId>
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.nimbusds</groupId>
|
||||||
|
<artifactId>nimbus-jose-jwt</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
@ -8,4 +8,7 @@ public interface ESConstants {
|
|||||||
|
|
||||||
String INDEX_LOGIN_PREFIX = "youlai-auth-login-";
|
String INDEX_LOGIN_PREFIX = "youlai-auth-login-";
|
||||||
|
|
||||||
|
|
||||||
|
String INDEX_LOGIN_PATTERN = "youlai-auth-login-*";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import cn.hutool.core.convert.Convert;
|
|||||||
import com.youlai.admin.common.constant.ESConstants;
|
import com.youlai.admin.common.constant.ESConstants;
|
||||||
import com.youlai.common.elasticsearch.service.ElasticSearchService;
|
import com.youlai.common.elasticsearch.service.ElasticSearchService;
|
||||||
import com.youlai.common.result.Result;
|
import com.youlai.common.result.Result;
|
||||||
import com.youlai.common.web.util.IpUtils;
|
import com.youlai.common.web.util.IPUtils;
|
||||||
import com.youlai.common.web.util.RequestUtils;
|
import com.youlai.common.web.util.RequestUtils;
|
||||||
import io.swagger.annotations.Api;
|
import io.swagger.annotations.Api;
|
||||||
import io.swagger.annotations.ApiOperation;
|
import io.swagger.annotations.ApiOperation;
|
||||||
@ -72,7 +72,7 @@ public class DashboardController {
|
|||||||
|
|
||||||
// 当前用户统计
|
// 当前用户统计
|
||||||
HttpServletRequest request = RequestUtils.getRequest();
|
HttpServletRequest request = RequestUtils.getRequest();
|
||||||
String clientIP = IpUtils.getIpAddr(request);
|
String clientIP = IPUtils.getClientIP(request);
|
||||||
|
|
||||||
boolQueryBuilder.must(QueryBuilders.termQuery("clientIP", clientIP));
|
boolQueryBuilder.must(QueryBuilders.termQuery("clientIP", clientIP));
|
||||||
Map<String, Long> myCountMap = elasticSearchService.dateHistogram(boolQueryBuilder, "date", DateHistogramInterval.days(1), indices);
|
Map<String, Long> myCountMap = elasticSearchService.dateHistogram(boolQueryBuilder, "date", DateHistogramInterval.days(1), indices);
|
||||||
@ -97,4 +97,5 @@ public class DashboardController {
|
|||||||
return Result.success(map);
|
return Result.success(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,92 @@
|
|||||||
|
package com.youlai.admin.controller;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.youlai.admin.common.constant.ESConstants;
|
||||||
|
import com.youlai.admin.pojo.domain.LoginRecord;
|
||||||
|
import com.youlai.common.elasticsearch.service.ElasticSearchService;
|
||||||
|
import com.youlai.common.result.Result;
|
||||||
|
import io.swagger.annotations.Api;
|
||||||
|
import io.swagger.annotations.ApiImplicitParam;
|
||||||
|
import io.swagger.annotations.ApiImplicitParams;
|
||||||
|
import io.swagger.annotations.ApiOperation;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.elasticsearch.index.query.BoolQueryBuilder;
|
||||||
|
import org.elasticsearch.index.query.QueryBuilders;
|
||||||
|
import org.elasticsearch.index.query.RangeQueryBuilder;
|
||||||
|
import org.elasticsearch.search.sort.FieldSortBuilder;
|
||||||
|
import org.elasticsearch.search.sort.SortOrder;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hxr
|
||||||
|
* @date 2021-03-09
|
||||||
|
*/
|
||||||
|
@Api(tags = "登录记录")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api.admin/v1/records/login")
|
||||||
|
@Slf4j
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class LoginRecordController {
|
||||||
|
|
||||||
|
ElasticSearchService elasticSearchService;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ApiOperation(value = "列表分页", httpMethod = "GET")
|
||||||
|
@ApiImplicitParams({
|
||||||
|
@ApiImplicitParam(name = "page", value = "页码", defaultValue = "1", paramType = "query", dataType = "Long"),
|
||||||
|
@ApiImplicitParam(name = "limit", value = "每页数量", defaultValue = "10", paramType = "query", dataType = "Long"),
|
||||||
|
@ApiImplicitParam(name = "startDate", value = "开始日期", paramType = "query", dataType = "String"),
|
||||||
|
@ApiImplicitParam(name = "endDate", value = "结束日期", paramType = "query", dataType = "String"),
|
||||||
|
@ApiImplicitParam(name = "clientIP", value = "客户端IP", paramType = "query", dataType = "String")
|
||||||
|
})
|
||||||
|
@GetMapping
|
||||||
|
public Result list(
|
||||||
|
Integer page,
|
||||||
|
Integer limit,
|
||||||
|
String startDate,
|
||||||
|
String endDate,
|
||||||
|
String clientIP
|
||||||
|
) {
|
||||||
|
|
||||||
|
// 日期范围
|
||||||
|
RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("date");
|
||||||
|
|
||||||
|
if (StrUtil.isNotBlank(startDate)) {
|
||||||
|
rangeQueryBuilder.from(startDate);
|
||||||
|
}
|
||||||
|
if (StrUtil.isNotBlank(endDate)) {
|
||||||
|
rangeQueryBuilder.to(endDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery().must(rangeQueryBuilder);
|
||||||
|
|
||||||
|
if (StrUtil.isNotBlank(clientIP)) {
|
||||||
|
queryBuilder.must(QueryBuilders.wildcardQuery("clientIP", "*" + clientIP + "*"));
|
||||||
|
}
|
||||||
|
// 总记录数
|
||||||
|
long count = elasticSearchService.count(queryBuilder, ESConstants.INDEX_LOGIN_PATTERN);
|
||||||
|
|
||||||
|
// 排序
|
||||||
|
FieldSortBuilder sortBuilder = new FieldSortBuilder("@timestamp").order(SortOrder.DESC);
|
||||||
|
|
||||||
|
// 分页数
|
||||||
|
List<LoginRecord> list = elasticSearchService.search(queryBuilder, sortBuilder, page, limit, LoginRecord.class, ESConstants.INDEX_LOGIN_PATTERN);
|
||||||
|
return Result.success(list, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ApiOperation(value = "删除登录记录", httpMethod = "DELETE")
|
||||||
|
@ApiImplicitParam(name = "ids", value = "id集合", required = true, paramType = "query", dataType = "String")
|
||||||
|
@DeleteMapping("/{ids}")
|
||||||
|
public Result delete(@PathVariable String ids) {
|
||||||
|
return Result.judge(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
package com.youlai.admin.controller;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import com.nimbusds.jose.JWSObject;
|
||||||
|
import com.youlai.common.constant.AuthConstants;
|
||||||
|
import com.youlai.common.result.Result;
|
||||||
|
import io.swagger.annotations.Api;
|
||||||
|
import io.swagger.annotations.ApiImplicitParam;
|
||||||
|
import io.swagger.annotations.ApiOperation;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.logging.log4j.util.Strings;
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author hxr
|
||||||
|
* @date 2021-03-09
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Api(tags = "令牌接口")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api.admin/v1/tokens")
|
||||||
|
@Slf4j
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class TokenController {
|
||||||
|
|
||||||
|
RedisTemplate redisTemplate;
|
||||||
|
|
||||||
|
@ApiOperation(value = "强制下线", httpMethod = "POST")
|
||||||
|
@ApiImplicitParam(name = "token", value = "访问令牌", required = true, paramType = "query", dataType = "String")
|
||||||
|
@PostMapping("/{token}/_invalid")
|
||||||
|
@SneakyThrows
|
||||||
|
public Result invalidToken(@PathVariable String token) {
|
||||||
|
|
||||||
|
token = token.replace(AuthConstants.AUTHORIZATION_PREFIX, Strings.EMPTY);
|
||||||
|
JWSObject jwsObject = JWSObject.parse(token);
|
||||||
|
String payload = jwsObject.getPayload().toString();
|
||||||
|
|
||||||
|
JSONObject jsonObject = JSONUtil.parseObj(payload);
|
||||||
|
long currentTimeSeconds = System.currentTimeMillis() / 1000;
|
||||||
|
|
||||||
|
String jti = jsonObject.getStr(AuthConstants.JWT_JTI); // JWT唯一标识
|
||||||
|
long exp = jsonObject.getLong(AuthConstants.JWT_EXP); // JWT过期时间戳
|
||||||
|
|
||||||
|
if (exp < currentTimeSeconds) { // token已过期,无需加入黑名单
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
redisTemplate.opsForValue().set(AuthConstants.TOKEN_BLACKLIST_PREFIX + jti, null, (exp - currentTimeSeconds), TimeUnit.SECONDS);
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -38,7 +38,6 @@ public class UserController extends BaseController {
|
|||||||
|
|
||||||
private final ISysUserService iSysUserService;
|
private final ISysUserService iSysUserService;
|
||||||
private final ISysUserRoleService iSysUserRoleService;
|
private final ISysUserRoleService iSysUserRoleService;
|
||||||
private final ISysRoleService iSysRoleService;
|
|
||||||
private final PasswordEncoder passwordEncoder;
|
private final PasswordEncoder passwordEncoder;
|
||||||
|
|
||||||
private final ISysPermissionService iSysPermissionService;
|
private final ISysPermissionService iSysPermissionService;
|
||||||
|
@ -1,16 +1,11 @@
|
|||||||
package com.youlai.admin;
|
package com.youlai.admin;
|
||||||
|
|
||||||
import com.youlai.common.elasticsearch.service.ElasticSearchService;
|
import com.youlai.common.elasticsearch.service.ElasticSearchService;
|
||||||
import com.youlai.common.web.pojo.domain.LoginLog;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
@SpringBootTest
|
@SpringBootTest
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class ElasticSearchTests {
|
public class ElasticSearchTests {
|
||||||
@ -18,22 +13,9 @@ public class ElasticSearchTests {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private ElasticSearchService elasticSearchService;
|
private ElasticSearchService elasticSearchService;
|
||||||
|
|
||||||
@Test
|
|
||||||
public void search() {
|
|
||||||
List<LoginLog> list = elasticSearchService.search(null, LoginLog.class, "youlai-auth-login-2021-03-06");
|
|
||||||
System.out.println(list.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void count() {
|
public void count() {
|
||||||
long count = elasticSearchService.count(null, "youlai-auth-login-2021-03-06");
|
long count = elasticSearchService.count(null, "youlai-auth-login-2021-03-06");
|
||||||
log.info(String.valueOf(count));
|
log.info(String.valueOf(count));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void group(){
|
|
||||||
Map<String, Long> map = elasticSearchService.dateHistogram(null, "date",DateHistogramInterval.days(1),"youlai-auth-login-2021-03-07","youlai-auth-login-2021-03-08");
|
|
||||||
log.info(map.toString());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -27,8 +27,8 @@ public class LogoutController {
|
|||||||
@DeleteMapping("/logout")
|
@DeleteMapping("/logout")
|
||||||
public Result logout() {
|
public Result logout() {
|
||||||
JSONObject jsonObject = RequestUtils.getJwtPayload();
|
JSONObject jsonObject = RequestUtils.getJwtPayload();
|
||||||
String jti = jsonObject.getStr("jti"); // JWT唯一标识
|
String jti = jsonObject.getStr(AuthConstants.JWT_JTI); // JWT唯一标识
|
||||||
long exp = jsonObject.getLong("exp"); // JWT过期时间戳
|
long exp = jsonObject.getLong(AuthConstants.JWT_EXP); // JWT过期时间戳
|
||||||
|
|
||||||
long currentTimeSeconds = System.currentTimeMillis() / 1000;
|
long currentTimeSeconds = System.currentTimeMillis() / 1000;
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ public interface AuthConstants {
|
|||||||
/**
|
/**
|
||||||
* JWT令牌前缀
|
* JWT令牌前缀
|
||||||
*/
|
*/
|
||||||
String JWT_PREFIX = "bearer ";
|
String AUTHORIZATION_PREFIX = "bearer ";
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -24,6 +24,16 @@ public interface AuthConstants {
|
|||||||
*/
|
*/
|
||||||
String JWT_PAYLOAD_KEY = "payload";
|
String JWT_PAYLOAD_KEY = "payload";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JWT ID 唯一标识
|
||||||
|
*/
|
||||||
|
String JWT_JTI = "jti";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JWT ID 唯一标识
|
||||||
|
*/
|
||||||
|
String JWT_EXP = "exp";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Redis缓存权限规则key
|
* Redis缓存权限规则key
|
||||||
*/
|
*/
|
||||||
@ -84,4 +94,9 @@ public interface AuthConstants {
|
|||||||
|
|
||||||
|
|
||||||
String LOGOUT_PATH = "/youlai-auth/oauth/logout";
|
String LOGOUT_PATH = "/youlai-auth/oauth/logout";
|
||||||
|
|
||||||
|
|
||||||
|
String GRANT_TYPE_KEY = "grant_type";
|
||||||
|
|
||||||
|
String REFRESH_TOKEN = "refresh_token";
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ import org.springframework.context.annotation.Configuration;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ela
|
* ElasticSearch HighLevelClient
|
||||||
*
|
*
|
||||||
* @author hxr
|
* @author hxr
|
||||||
* @date 2021-03-05
|
* @date 2021-03-05
|
||||||
|
@ -21,6 +21,7 @@ import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInter
|
|||||||
import org.elasticsearch.search.aggregations.bucket.histogram.Histogram;
|
import org.elasticsearch.search.aggregations.bucket.histogram.Histogram;
|
||||||
import org.elasticsearch.search.aggregations.bucket.histogram.ParsedDateHistogram;
|
import org.elasticsearch.search.aggregations.bucket.histogram.ParsedDateHistogram;
|
||||||
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
||||||
|
import org.elasticsearch.search.sort.SortBuilder;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@ -39,12 +40,9 @@ public class ElasticSearchService {
|
|||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public long count(QueryBuilder queryBuilder, String... indices) {
|
public long count(QueryBuilder queryBuilder, String... indices) {
|
||||||
// 构造搜索条件
|
|
||||||
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
|
|
||||||
searchSourceBuilder.query(queryBuilder);
|
|
||||||
// 构造请求
|
// 构造请求
|
||||||
CountRequest countRequest = new CountRequest(indices);
|
CountRequest countRequest = new CountRequest(indices);
|
||||||
countRequest.source(searchSourceBuilder);
|
countRequest.query(queryBuilder);
|
||||||
// 执行请求
|
// 执行请求
|
||||||
CountResponse countResponse = client.count(countRequest, RequestOptions.DEFAULT);
|
CountResponse countResponse = client.count(countRequest, RequestOptions.DEFAULT);
|
||||||
long count = countResponse.getCount();
|
long count = countResponse.getCount();
|
||||||
@ -53,6 +51,7 @@ public class ElasticSearchService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 日期统计
|
* 日期统计
|
||||||
|
*
|
||||||
* @param queryBuilder 查询条件
|
* @param queryBuilder 查询条件
|
||||||
* @param field 聚合字段,如:登录日志的 date 字段
|
* @param field 聚合字段,如:登录日志的 date 字段
|
||||||
* @param interval 统计时间间隔,如:1天、1周
|
* @param interval 统计时间间隔,如:1天、1周
|
||||||
@ -107,11 +106,25 @@ public class ElasticSearchService {
|
|||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public <T> List<T> search(QueryBuilder queryBuilder, Class<T> clazz, String... indices) {
|
public <T> List<T> search(QueryBuilder queryBuilder, Class<T> clazz, String... indices) {
|
||||||
|
List<T> list = this.search(queryBuilder, null, 1, ESConstants.DEFAULT_PAGE_SIZE, clazz, indices);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public <T> List<T> search(QueryBuilder queryBuilder, Integer page, Integer size, Class<T> clazz, String... indices) {
|
||||||
|
List<T> list = this.search(queryBuilder, null, 1, ESConstants.DEFAULT_PAGE_SIZE, clazz, indices);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public <T> List<T> search(QueryBuilder queryBuilder, SortBuilder sortBuilder, Integer page, Integer size, Class<T> clazz, String... indices) {
|
||||||
// 构造SearchSourceBuilder
|
// 构造SearchSourceBuilder
|
||||||
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
|
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
|
||||||
searchSourceBuilder.query(queryBuilder);
|
searchSourceBuilder.query(queryBuilder);
|
||||||
searchSourceBuilder.from(0);
|
searchSourceBuilder.sort(sortBuilder);
|
||||||
searchSourceBuilder.size(ESConstants.DEFAULT_PAGE_SIZE);
|
searchSourceBuilder.from((page - 1) * size);
|
||||||
|
searchSourceBuilder.size(size);
|
||||||
// 构造SearchRequest
|
// 构造SearchRequest
|
||||||
SearchRequest searchRequest = new SearchRequest(indices);
|
SearchRequest searchRequest = new SearchRequest(indices);
|
||||||
searchRequest.source(searchSourceBuilder);
|
searchRequest.source(searchSourceBuilder);
|
||||||
@ -128,5 +141,4 @@ public class ElasticSearchService {
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -28,12 +28,6 @@
|
|||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.github.dozermapper</groupId>
|
|
||||||
<artifactId>dozer-core</artifactId>
|
|
||||||
<version>6.2.0</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-aop</artifactId>
|
<artifactId>spring-boot-starter-aop</artifactId>
|
||||||
@ -51,6 +45,18 @@
|
|||||||
<artifactId>logstash-logback-encoder</artifactId>
|
<artifactId>logstash-logback-encoder</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.lionsoul</groupId>
|
||||||
|
<artifactId>ip2region</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.dozermapper</groupId>
|
||||||
|
<artifactId>dozer-core</artifactId>
|
||||||
|
<version>6.2.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
@ -1,13 +1,10 @@
|
|||||||
package com.youlai.common.web.aspect;
|
package com.youlai.common.web.aspect;
|
||||||
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.hutool.extra.servlet.ServletUtil;
|
|
||||||
import cn.hutool.json.JSON;
|
|
||||||
import cn.hutool.json.JSONObject;
|
import cn.hutool.json.JSONObject;
|
||||||
import cn.hutool.json.JSONUtil;
|
import cn.hutool.json.JSONUtil;
|
||||||
import com.youlai.common.constant.AuthConstants;
|
import com.youlai.common.constant.AuthConstants;
|
||||||
import com.youlai.common.web.pojo.domain.LoginLog;
|
import com.youlai.common.web.util.IPUtils;
|
||||||
import com.youlai.common.web.util.IpUtils;
|
|
||||||
import io.swagger.annotations.ApiOperation;
|
import io.swagger.annotations.ApiOperation;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@ -24,8 +21,9 @@ import org.springframework.web.context.request.RequestContextHolder;
|
|||||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import java.text.SimpleDateFormat;
|
import java.time.Duration;
|
||||||
import java.util.Date;
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author hxr
|
* @author hxr
|
||||||
@ -44,47 +42,50 @@ public class LoginLogAspect {
|
|||||||
|
|
||||||
@Around("Log()")
|
@Around("Log()")
|
||||||
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
|
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||||
// 时间统计
|
|
||||||
Date now = new Date();
|
|
||||||
long startTime = now.getTime();
|
|
||||||
Object result = joinPoint.proceed();
|
|
||||||
long endTime = System.currentTimeMillis();
|
|
||||||
long elapsedTime = endTime - startTime;
|
|
||||||
|
|
||||||
// 获取方法签名
|
LocalDateTime startTime = LocalDateTime.now();
|
||||||
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
Object result = joinPoint.proceed();
|
||||||
String description = signature.getMethod().getAnnotation(ApiOperation.class).value();
|
|
||||||
|
|
||||||
// 获取请求信息
|
// 获取请求信息
|
||||||
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||||
HttpServletRequest request = attributes.getRequest();
|
HttpServletRequest request = attributes.getRequest();
|
||||||
// String clientIP = ServletUtil.getClientIP(request);
|
|
||||||
String clientIP= IpUtils.getIpAddr(request);
|
// 刷新token不记录
|
||||||
String requestUrl = request.getRequestURL().toString();
|
String grantType=request.getParameter(AuthConstants.GRANT_TYPE_KEY);
|
||||||
String method = request.getMethod();
|
if(grantType.equals(AuthConstants.REFRESH_TOKEN)){
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 时间统计
|
||||||
|
LocalDateTime endTime = LocalDateTime.now();
|
||||||
|
long elapsedTime = Duration.between(startTime, endTime).toMillis(); // 请求耗时(毫秒)
|
||||||
|
|
||||||
|
// 获取接口描述信息
|
||||||
|
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||||
|
String description = signature.getMethod().getAnnotation(ApiOperation.class).value();// 方法描述
|
||||||
|
|
||||||
|
String username = request.getParameter(AuthConstants.USER_NAME_KEY); // 登录用户名
|
||||||
|
String date = startTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); // 索引名需要,因为默认生成索引的date时区不一致
|
||||||
|
|
||||||
|
// 获取token
|
||||||
|
String token = Strings.EMPTY;
|
||||||
|
if (request != null) {
|
||||||
|
JSONObject jsonObject = JSONUtil.parseObj(result);
|
||||||
|
token = jsonObject.getStr("value");
|
||||||
|
}
|
||||||
|
String clientIP = IPUtils.getClientIP(request); // 客户端请求IP(注意:如果使用Nginx代理需配置)
|
||||||
|
String region = IPUtils.ip2region(clientIP); // IP对应的城市信息
|
||||||
|
|
||||||
// MDC 扩展logback字段,具体请看logback-spring.xml的自定义日志输出格式
|
// MDC 扩展logback字段,具体请看logback-spring.xml的自定义日志输出格式
|
||||||
MDC.put("elapsedTime", StrUtil.toString(elapsedTime));
|
MDC.put("elapsedTime", StrUtil.toString(elapsedTime));
|
||||||
MDC.put("description", description);
|
MDC.put("description", description);
|
||||||
MDC.put("clientIP", clientIP);
|
MDC.put("region", region);
|
||||||
MDC.put("url", requestUrl);
|
|
||||||
MDC.put("method", method);
|
|
||||||
|
|
||||||
String username = request.getParameter(AuthConstants.USER_NAME_KEY);
|
|
||||||
MDC.put("username", username);
|
MDC.put("username", username);
|
||||||
|
|
||||||
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
|
||||||
String date = simpleDateFormat.format(now);
|
|
||||||
MDC.put("date", date);
|
MDC.put("date", date);
|
||||||
|
MDC.put("token", token);
|
||||||
|
MDC.put("clientIP", clientIP);
|
||||||
|
|
||||||
// 获取登录结果
|
log.info("{} 登录,耗费时间 {} 毫秒", username, elapsedTime); // 收集日志这里必须打印一条日志,内容随便吧,记录在message字段,具体看logback-spring.xml文件
|
||||||
String accessToken = Strings.EMPTY;
|
|
||||||
if (request != null) {
|
|
||||||
JSONObject jsonObject = JSONUtil.parseObj(result);
|
|
||||||
accessToken = jsonObject.getStr("value");
|
|
||||||
}
|
|
||||||
MDC.put("accessToken", accessToken);
|
|
||||||
log.info("{} 登录,耗费时间 {} 毫秒", username, elapsedTime); // 收集日志这里必须打印一条日志,内容随便
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
package com.youlai.common.web.pojo.domain;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Author haoxr
|
|
||||||
* @Date 2021-03-01 16:45
|
|
||||||
* @Version 1.0.0
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class LoginLog {
|
|
||||||
|
|
||||||
private String description;
|
|
||||||
|
|
||||||
private String clientIP;
|
|
||||||
|
|
||||||
private String url;
|
|
||||||
|
|
||||||
private String method;
|
|
||||||
|
|
||||||
private long elapsedTime;
|
|
||||||
|
|
||||||
private Object result;
|
|
||||||
|
|
||||||
}
|
|
@ -2,20 +2,29 @@ package com.youlai.common.web.util;
|
|||||||
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.logging.log4j.util.Strings;
|
||||||
|
import org.lionsoul.ip2region.DataBlock;
|
||||||
|
import org.lionsoul.ip2region.DbConfig;
|
||||||
|
import org.lionsoul.ip2region.DbSearcher;
|
||||||
|
import org.lionsoul.ip2region.Util;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.io.File;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class IpUtils {
|
public class IPUtils {
|
||||||
|
|
||||||
|
private static String LOCAL_IP="127.0.0.1";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取IP地址
|
* 获取IP地址
|
||||||
* 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址
|
* 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址
|
||||||
* 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址
|
* 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址
|
||||||
*/
|
*/
|
||||||
public static String getIpAddr(HttpServletRequest request) {
|
public static String getClientIP(HttpServletRequest request) {
|
||||||
String ip = null;
|
String ip = null;
|
||||||
try {
|
try {
|
||||||
if (request == null) {
|
if (request == null) {
|
||||||
@ -46,11 +55,9 @@ public class IpUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//使用代理,则获取第一个IP地址
|
//使用代理,则获取第一个IP地址
|
||||||
if (StrUtil.isEmpty(ip) && ip.length() > 15) {
|
if (StrUtil.isNotBlank(ip) && ip.indexOf(",") > 0) {
|
||||||
if (ip.indexOf(",") > 0) {
|
|
||||||
ip = ip.substring(0, ip.indexOf(","));
|
ip = ip.substring(0, ip.indexOf(","));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return ip;
|
return ip;
|
||||||
}
|
}
|
||||||
@ -71,4 +78,43 @@ public class IpUtils {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析IP获取城市区域信息
|
||||||
|
*
|
||||||
|
* @param ip
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String ip2region(String ip) {
|
||||||
|
if (Util.isIpAddress(ip) == false) {
|
||||||
|
return Strings.EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(LOCAL_IP.equals(ip)){
|
||||||
|
return "本地访问";
|
||||||
|
}
|
||||||
|
|
||||||
|
String filePath = IPUtils.class.getResource("/data/ip2region.db").getPath();
|
||||||
|
File file = new File(filePath);
|
||||||
|
if (file.exists() == false) {
|
||||||
|
return Strings.EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
DbConfig config = new DbConfig();
|
||||||
|
DbSearcher searcher = new DbSearcher(config, filePath);
|
||||||
|
|
||||||
|
Method method = searcher.getClass().getMethod("btreeSearch", String.class);
|
||||||
|
DataBlock dataBlock = (DataBlock) method.invoke(searcher, ip);
|
||||||
|
|
||||||
|
return dataBlock.getRegion();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return Strings.EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user