SpringCloud微服务安全API安全 2-8 授权
Posted 鮀城小帅
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringCloud微服务安全API安全 2-8 授权相关的知识,希望对你有一定的参考价值。
登录
- 登录的本质
- 保持登录状态的方法
- 常见的登录攻击及防护
1. 登录
在认证部分用过滤器实现HttpBasic 认证 ,在请求头里携带用户名和密码,存在的问题是,你不可能让用户每个请求都输入用户名密码吧,即使前端把用户名密码存起来,这也是不安全的。
1.1 基于Token的身份认证的实现
1.2 基于cookie和session的实现
2. 登录
2.1 登录接口
@GetMapping("/login")
public void login(@Validated UserInfo user, HttpServletRequest request,HttpServletResponse response){
// 登录
UserInfo info = userService.login(user);
request.getSession().setAttribute("user",info);
}
2.2 授权拦截重构
登录操作不需要拦截
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
boolean result = true;
// 登录:该操作不做授权的校验
if (!ArrayUtils.contains(permitUrls,request.getRequestURI())){
User user = (User)request.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;
}
登录操作 result 返回true;其他请求则进行授权校验。
2.3 访问登录接口
记录JSESSIONID
2.4 再次请求,浏览器Cookie携带JSESSIONID到服务器。
2.5 基于cookie-session的登录方式的优劣势
优点:就是弥补了HttpBasic的缺点,使用起来很方便
缺点:只适用于浏览器,浏览器接收到Response Header里的jessionId后去设置 Cookie,无法对App、第三方的服务器,因为他们不认Cookie
集群环境下,需要进行Session共享处理
3. 自定义Token实现
主要通过基于 Token实现身份认证
4. 防范Session固定攻击
@GetMapping("/login")
public void login(@Validated UserInfo user, HttpServletRequest request,HttpServletResponse response){
// 登录
UserInfo info = userService.login(user);
// request.getSession().setAttribute("user",info);
// 优化,解决Session固定攻击
HttpSession session = request.getSession(false); // 将当前请求的sessionId失效
if(session != null ){
session.invalidate();
}
// true用于标识生成新的JSESSIONID
request.getSession(true).setAttribute("user", user);
}
5.代码重构
重构内容:
- 同时提供 Authorization 和 cookie-session 两种授权策略
- 所有接口都允许使用 authorization 策略进行请求
- cookie-session 策略由 login() 接口登录后生成 cookie ,其他的接口携带 cookie 进行请求
5.1 认证功能重构
BasicAuthecationFilter.java
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// 获取请求头中的认证信息
String authHeader = request.getHeader("Authorization");
// 请求进来的时候调用
// 判断认证信息是否存在,有就进行验证
if(StringUtils.isNotBlank(authHeader)){
// 截取认证信息中的basic信息
String token64 = StringUtils.substringAfter(authHeader,"Basic ");
String token = new String(Base64Utils.decodeFromString(token64));
String[] items = StringUtils.splitByWholeSeparatorPreserveAllTokens(token,":");
String username = items[0];
String password = items[1];
User user = userRepository.findByUsername(username);
// 当前用户不为空与密码正确的情况下,认证通过,并将相关用户数据存入到请求域中
boolean bo = SCryptUtil.check(password,user.getPassword());
// if(user != null && StringUtils.equals(password , user.getPassword())){
if(user != null && bo){
//认证通过,存放用户信息
request.getSession().setAttribute("user", user.buildInfo());
request.getSession().setAttribute("temp","yes");
}
}
try {
//不管认证是否正确,继续往下走,是否可以访问,交给授权处理
filterChain.doFilter(request, response);
}finally {
// finally 中是响应的时候调用
HttpSession session = request.getSession();
if (session.getAttribute("temp")!= null){
session.invalidate();
}
}
}
5.2 授权功能重构
AclInterceptor.java
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
boolean result = true;
// 登录:该操作不做授权的校验
if (!ArrayUtils.contains(permitUrls,request.getRequestURI())){
UserInfo userInfo = (UserInfo)request.getSession().getAttribute("user");
if(userInfo == null){
response.setContentType("text/plain");
response.getWriter().write("need authentication");
response.setStatus(HttpStatus.UNAUTHORIZED.value());
result = false;
}else{
String method = request.getMethod();
if(!userInfo.hasPermission(method)){
response.setContentType("text/plain");
response.getWriter().write("forbidden");
response.setStatus(HttpStatus.FORBIDDEN.value());
result = false;
}
}
}
return result;
}
5.3 RW权限校验重构
UserInfo.java , 这里主要将 User 和 UserInfo 做分离
// @NotBlank(message = "权限不能为空")
private String permissions;
/**
* @Description TODO 根据请求的method来判断是否有访问当前接口的权限
* @param method
* @return
*/
public boolean hasPermission(String method) {
boolean result = false;
// 判断该权限是读还是写
if(StringUtils.equalsIgnoreCase("get",method)){
result = StringUtils.contains(permissions, "r");
}else {
result = StringUtils.contains(permissions,"w");
}
return result;
}
5.4重构当前用户获取的逻辑
SecurityConfig.java ,这里主要提供审计日志记录所用的用户信息,重构后更加灵活。
@Bean
public AuditorAware<String> auditorAware(){
return new AuditorAware<String>() {
@Override
public Optional<String> getCurrentAuditor() {
// 从session中拿到用户信息
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
UserInfo info = (UserInfo) servletRequestAttributes.getRequest().getSession().getAttribute("user");
String username = null;
if (info != null){
username = info.getUsername();
}
// return Optional.of("wsp");
return Optional.ofNullable(username);
}
};
}
5.5 重构后测试
(1)由 login() 接口生成 cookie
(2)例:查询接口携带Cookie 请求
以上是关于SpringCloud微服务安全API安全 2-8 授权的主要内容,如果未能解决你的问题,请参考以下文章
SpringCloud微服务安全API安全 2-2 注入攻击防护
SpringCloud微服务安全实战API安全 3-9 总结