认证 vs 登录
- 认证和登录不一样,登录是获取用户信息,认证是验证用户身份是否合法;
- 登录的行为,在一段时间内,往往只发生一次;认证的行为,是每次请求调用业务逻辑的时候,都会做的;
- 认证不管成没成功,都会往下走,进入审计步骤去记录;最终请求能不能通过,是需要授权机制去决定的,比如认证机制没生效,不知道当前用户是谁,但是这个请求任然可能是可以调用业务逻辑的,比如商品查询;登录如果没成功,就不会往下走了;
基于 HTTP 的认证 | HTTP Basic 认证
步骤
- 准备认证信息:用户名、密码;
- 对认证信息做 Base64 加密;
- 把加密后的内容填入 HTTP 的 Authorization 头,以 Basic 开头;
- 认证没有通过的话,要返回 401;
代码实现
package com.lixinlei.security.api.filter;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import com.lixinlei.security.api.dao.UserRepository;
import com.lixinlei.security.api.entity.User;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.Base64Utils;
import org.springframework.web.filter.OncePerRequestFilter;
import com.lambdaworks.crypto.SCryptUtil;
@Component
@Order(2)
public class BasicAuthecationFilter extends OncePerRequestFilter {
@Autowired
private UserRepository userRepository;
/**
* Authorization 中携带的认证信息,是 username:password 经过 Base64 加密之后的值;
* 该 Filter 验证的是,username:password 中的信息是否指向同一个用户;
* @param request
* @param response
* @param filterChain
* @throws ServletException
* @throws IOException
*/
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
System.out.println(2);
// 这里从 Authorization 中取出的,就直接是带了 Basic 开头的,经过 Base64 加密了的字符串
String authHeader = request.getHeader("Authorization");
if(StringUtils.isNotBlank(authHeader)) {
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);
if(user != null && SCryptUtil.check(password, user.getPassword())) {
request.getSession().setAttribute("user", user.buildInfo());
request.getSession().setAttribute("temp", "yes");
}
}
// 即使认证没通过,请求也要放行到下一个安全机制:审计
try {
filterChain.doFilter(request, response);
} finally {
HttpSession session = request.getSession();
if(session.getAttribute("temp") != null) {
session.invalidate();
}
}
}
}
业务接口
/**
* 想要获取的用户信息,必须和 Authorization 中的用户信息一致;
* @param id 用这次请求想要获取的用户的 Id;
* @param request request.Session 中存放的用户信息,是请求头 Authorization 中携带的,在之前的 Filter 中解析出来存放进 Session 的;
* @return
* @throws IOException
*/
@GetMapping("/{id}")
public UserInfo get(@PathVariable Long id, HttpServletRequest request) throws IOException {
UserInfo user = (UserInfo) request.getSession().getAttribute("user");
if(user == null || !user.getId().equals(id)) {
throw new RuntimeException("身份认证信息异常,获取用户信息失败");
}
return userService.get(id);
}
网友评论