API安全(四)-认证

我的未来我决定 提交于 2020-01-21 17:23:42

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!