API安全
基础知识点
- 请求未被授权,则应该返回403状态码
 - 请求验证需要认证,但未认证,则应返回401状态码。比如:没有携带认证信息,或者携带的信息错误
 - 请求数过多,返回429
 
实现访问控制有两种方法
- ACL: Access Control Lists
    
- 简单易用,实现容易,但不易管理。比如:linux的用户管理,需要对每个用户设置它能干什么,读啊,写啊的权限
 
 - RBAC: Role Based Access Control
    
- 引入角色概念,简化管理。但开发较为复杂
 
 
SQL注入攻击
mybatis 有两种参数的赋值方法。#{} 和 ${},其中#{}会将参数转换为字符串传递,而${}是原样输入,容易造成SQL注入攻击
过滤器、拦截器
过滤器、拦截器及spring mvc 的分发器servlet的执行顺序



请求认证基础流程

- 流控 : 流量控制,保证系统可用。整个安全机制最前面
 - 认证 : 校验用户是否存在
 - 审计 : 统计请求信息,谁在什么时候干了什么事儿
 - 授权 : 给登录的用户授权
 
常用验证
Token验证

Cookie-Session验证

Session攻击

Session攻击解决方法
// 每次登录的时候,都将对应Session设置为失效
// 然后重新生成SessoinId,存储对应信息
UserInfo info = userService.login(user);
HttpSession session = request.getSession(false);
if(session != null) {
	session.invalidate();
}
request.getSession(true).setAttribute("user", info);
流控 - 限流过滤器实现
package com.imooc.security.filter;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import com.google.common.util.concurrent.RateLimiter;
@Component
// 注解@Order或者接口Ordered的作用是定义
// Spring IOC容器中Bean的执行顺序的优先级,而不是定义Bean的加载顺序,
// Bean的加载顺序不受@Order或Ordered接口的影响
@Order(1)
// OncePerRequestFilter
// 它能够确保在一次请求中只通过一次filter,而不需要重复执行
// 比如:
// 在servlet-2.3中,Filter会过滤一切请求,包括服务器内部使用forward转发请求
// 和<%@ include file="/index.jsp"%>的情况。
//
// 到了servlet-2.4中Filter默认下只拦截外部提交的请求,forward和include
// 这些内部转发都不会被过滤,但是有时候我们需要 forward的时候也用到Filter
public class RateLimitFilter extends OncePerRequestFilter {
	// Google开源工具包Guava提供了限流工具类RateLimiter
	// 该类基于令牌桶算法(Token Bucket)来完成限流
	// 参考
	// https://www.jianshu.com/p/693031015940
	private RateLimiter rateLimiter = RateLimiter.create(1);
	/* (non-Javadoc)
	 * @see org.springframework.web.filter.OncePerRequestFilter#doFilterInternal(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, javax.servlet.FilterChain)
	 */
	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {
		System.out.println(1);
		if(rateLimiter.tryAcquire()) {
			filterChain.doFilter(request, response);
		}else {
			response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
			response.getWriter().write("too many request!!!");
			response.getWriter().flush();
			return;
		}
	}
}
拦截器实现
继承HandlerInterceptorAdapter类,实现preHandle方法
// 不用复写postHandle,这个方法是在请求结束的时候执行的
@Component
public class AclInterceptor extends HandlerInterceptorAdapter {
	private String[] permitUrls = new String[] {"/users/login"};
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		System.out.println(4);
		boolean result = true;
		if(!ArrayUtils.contains(permitUrls, request.getRequestURI())) {
			UserInfo user = (UserInfo) request.getSession().getAttribute("user");
			if(user == null) {
				response.setContentType("text/plain");
				response.getWriter().write("need authentication");
				response.setStatus(HttpStatus.UNAUTHORIZED.value());
				result = false;
			}else {
				String method = request.getMethod();
				if(!user.hasPermission(method)) {
					response.setContentType("text/plain");
					response.getWriter().write("forbidden");
					response.setStatus(HttpStatus.FORBIDDEN.value());
					result = false;
				}
			}
		}
		return result;
	}
}
若有多个拦截器,需要配置拦截器的顺序
- 实现WebMvcConfigurer类
 - 复写addInterceptors方法
 
@Configuration
// 审计功能使用
@EnableJpaAuditing
public class SecurityConfig implements WebMvcConfigurer {
	@Autowired
	private AuditLogInterceptor auditLogInterceptor;
	@Autowired
	private AclInterceptor aclInterceptor;
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		registry.addInterceptor(auditLogInterceptor);
		registry.addInterceptor(aclInterceptor);
	}
	// 审计功能使用
	@Bean
	public AuditorAware<String> auditorAware() {
		return new AuditorAware<String>() {
			@Override
			public Optional<String> getCurrentAuditor() {
				ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
				UserInfo info = (UserInfo)servletRequestAttributes.getRequest().getSession().getAttribute("user");
				String username = null;
				if(info != null) {
					username = info.getUsername();
				}
				return Optional.ofNullable(username);
			}};
	}
}
全局异常检验
@RestControllerAdvice
@Slf4j
public class ErrorHandler {
	// 抛出异常的时候指定校验返回码为500
	@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
	@ExceptionHandler(Exception.class)
	public Map<String, Object> handle(Exception ex){
		log.error("system error", ex);
		Map<String, Object> info = new HashMap<>();
		info.put("message", ex.getMessage());
		info.put("time", new Date().getTime());
		return info;
	}
}