五、API安全机制-审计(日志)
2020-05-13 本文已影响0人
紫荆秋雪_文
源码下载
一、API安全机制-审计(日志) API安全机制.png
审计日志应该在认证处理之后,这样我们就知道谁在发送请求,响应也应该被记录,尤其是请求被拒绝的时候
二、使用@Order注解来控制过滤器的执行顺序 过滤器顺气.png
- 这个顺序是不符合安全机制的,所以要使用@Order来控制过滤器调用顺序
/**
* 限流过滤器
*/
@Slf4j
@Order(1)
@Component
public class RateLimitFilter extends OncePerRequestFilter {
/**
* 限制1秒只能通过1个请求
*/
private RateLimiter rateLimiter = RateLimiter.create(1);
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
logger.info("1-限流过滤器");
if (this.rateLimiter.tryAcquire()) {
filterChain.doFilter(request, response);
}
else {
// 限流
response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
response.getWriter().write("Too Many Request !!!");
response.getWriter().flush();
return;
}
}
}
/**
* 请求认证过滤器
*/
@Slf4j
@Order(2)
@Component
public class AuthenticationFilter extends OncePerRequestFilter {
@Autowired
private IRavenUserRepository userRepository;
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
logger.info("2-认证过滤器");
String header = request.getHeader("Authorization");
if (StringUtils.isNotBlank(header)) {
String token64 = StringUtils.substringAfter(header, "Basic ");
String token = new String(Base64Utils.decodeFromString(token64));
String[] items = StringUtils.splitByWholeSeparatorPreserveAllTokens(token, ":");
if (items.length != 2) {
log.info("用户身份认证错误!!!");
throw new RuntimeException("用户身份认证错误!!!");
}
String username = items[0];
String password = items[1];
RavenUser user = this.userRepository.findByName(username);
if (user != null && user.getPassword().equals(password)) {
request.setAttribute("user", user);
}
}
filterChain.doFilter(request, response);
}
}
- 打印结果
com.raven.security.user.web.filter.RateLimitFilter - 1-限流过滤器
c.r.security.user.web.filter.AuthenticationFilter - 2-认证过滤器
三、Filter、Interceptor、ControllerAdvice、AOP执行顺序 执行顺序.png
- 定义日志拦截器来拦截请求URL和响应
- RavenAuditLog
/**
* 审核日志
*/
@Data
@Entity
@EntityListeners(AuditingEntityListener.class)
@Table(name = "t_auditLog")
public class RavenAuditLog {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String method;
private String path;
private Integer status;
@Temporal(TemporalType.TIMESTAMP)
@CreatedDate
private Date createdTime;
@Temporal(TemporalType.TIMESTAMP)
@LastModifiedDate
private Date modifyTime;
}
- RavenAuditLogInterceptor日志拦截器
/**
* 日志拦截器
*/
@Component
public class RavenAuditLogInterceptor extends HandlerInterceptorAdapter {
@Autowired
private IRavenAuditLogService auditLogService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
RavenAuditLog log = new RavenAuditLog();
log.setMethod(request.getMethod());
log.setPath(request.getRequestURI());
RavenUser user = (RavenUser) request.getAttribute("user");
if (user != null) {
log.setUsername(user.getUsername());
}
// 保存日志
this.auditLogService.save(log);
request.setAttribute("auditLogId", log.getId());
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
Long auditLogId = (Long) request.getAttribute("auditLogId");
RavenAuditLog auditLog = this.auditLogService.selectById(auditLogId);
auditLog.setStatus(response.getStatus());
// 保存日志
this.auditLogService.save(auditLog);
}
}
- RavenInterceptorConfig拦截器生效配置类
/**
* 拦截器配置类
*/
@Configuration
@EnableJpaAuditing
public class RavenInterceptorConfig implements WebMvcConfigurer {
@Autowired
private RavenAuditLogInterceptor auditLogInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
/**
* registry.addInterceptor(this.auditLogInterceptor).addPathPatterns("/user");
* 拦截器指定拦截的url
*/
registry.addInterceptor(this.auditLogInterceptor);
}
}
-
数据日志记录
日志记录.png
在请求url /users/2时,请求错误本应该只返回状态码500,但是确先返回状态码200,然后又跳转到/error返回状态码500,这样为分析错误会有很大难度,期望是请求/users/2时就直接返回状态码500
-
解决方案,通过统一处理错误处理器来处理
/**
* 处理全局异常
*/
@RestControllerAdvice
public class ErrorHandler {
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(Exception.class)
public Map<String, Object>handle(Exception e) {
HashMap<String, Object> map = new HashMap<>();
map.put("message", e.getMessage());
map.put("time", new Date().getTime());
return map;
}
}