1、什么是认证
认证是指我们去验证用户身份是否合法的过程。
2、认证和登陆的区别
很多人会把认证和登陆混为一谈,其实两者完全是两个概念。认证不是登陆,登陆是指用户获取身份证明的一个过程,认证是指我们去验证这个用户身份是否合法的过程。
登陆的行为,往往只发生一次,登陆成功后,会保存一段时间用户信息。而认证,每次请求去调用业务逻辑的时候都会去执行。
再有就是登陆一旦有问题,就不会继续往下走,比如说,登陆时用户名或密码填写错误了,这时会进行报错或返回,不会继续往下执行。而认证的话,不管认证信息是否正确,在整个安全机制链路中还是会继续往下执行(如下图),让后面的审计机制,去记录这次身份认证的结果是什么样子的。最终请求是不是可以被通过,要由授权来决定。而不是由认证来决定的。比如说当前请求没有用户认证信息或者认证机制根本没生效,但是这个请求,有可能是可以正常访问的。比如说首页,获取商品信息等。
3、HttpBasic认证
基于http协议的认证方式由很多,这里,我们先了解一下最简单的HttpBasic认证,这是一个最基础的认证。HttpBasic验证方案是在 RFC 7617中规定的,在该方案中,使用用户的 ID/密码作为凭证信息,并且使用 base64 算法进行编码。
步骤:3.1、用冒号将用户名和密码进行拼接(如:aladdin:opensesame)。
3.2、将第一步生成的结果用 base64 方式编码(YWxhZGRpbjpvcGVuc2VzYW1l)。
3.3、将编码后的字符串拼接上验证类型Basic 放入到请求头Authorization中(Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l)。
注意:对于需要在浏览器进行弹出输入用户名和密码框的,要在相应头中添加WWW-Authenticate并且Http状态码为401。
优点:简单,方便,所有的主流浏览器都支持。
缺点:Base64编码并不是一种加密方法或者hashing方法!这种方法的安全性与明文发送等同(base64可以逆向解码)。“基本验证”方案需要与HTTPS协议配合使用。
http身份认证参考文档:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Authentication
请求头Authorization参考文档:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Authorization
4、代码示例
4.1、数据准备
4.2、编写HttpBasic认证过滤器
/** * HttpBasic 认证 * * @author caofanqi * @date 2020/1/21 15:10 */ @Slf4j @Component @SuppressWarnings("ALL") public class BasicAuthorizationFilter extends OncePerRequestFilter { @Resource private UserRepository userRepository; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String authorizationHeader = request.getHeader("Authorization"); if (StringUtils.isNotBlank(authorizationHeader)) { String token64 = StringUtils.substringAfter(authorizationHeader, "Basic "); if (StringUtils.isNotBlank(token64)) { try { String token = new String(Base64Utils.decodeFromString(token64)); String[] items = StringUtils.splitByWholeSeparatorPreserveAllTokens(token, ":"); String username = items[0]; String password = items[1]; UserDO user = userRepository.findByUsername(username); if (user != null && StringUtils.equals(user.getPassword(), password)) { //认证通过,存放用户信息 request.setAttribute("user", user); } } catch (Exception e) { log.info("Basic Authorization Fail!"); } } } //不管认证是否正确,继续往下走,是否可以访问,交给授权处理 filterChain.doFilter(request, response); } }
4.3、因为我们还没有学习授权机制,所以这里使用代码来控制,UserController获取用户信息方法
@GetMapping("/{id}") public UserDTO get(@PathVariable Long id, HttpServletRequest request, HttpServletResponse response) throws IOException { /* * 因为还没有学习授权机制,这里写代码进行控制 */ UserDO user = (UserDO) request.getAttribute("user"); if (user == null){ //说明没有进行认证,返回401和WWW-Authenticate response.setStatus(HttpStatus.UNAUTHORIZED.value()); response.setHeader("WWW-Authenticate","Basic realm=<authentication required>"); return null; } if (!user.getId().equals(id)){ //只能查看自己的用户信息,不是本人,返回403 response.setStatus(HttpStatus.FORBIDDEN.value()); response.getWriter().write("permission denied"); response.getWriter().flush(); return null; } return userService.get(id); }
4.4、启动项目,访问http://127.0.0.1:9090/users/1,会弹出输入框,我们输入lisi的用户名和密码
这时我们查看请求头中已经带有Authorization认证信息,但是还是返回给我们403,那是因为lisi的id为2。
访问http://127.0.0.1:9090/users/2 ,可以正常获取用户信息
项目源码: https://github.com/caofanqi/study-security/tree/dev-httpbasic