• HTTP的几种认证方式之BASIC 认证(基本认证)


    HTTP/1.1 使用的认证方式有

      1)BASIC 认证(基本认证);

      2)DIGEST 认证(摘要认证);

      3)SSL 客户端认证;

      4)FormBase 认证(基于表单认证);

    本文目录:

      1、BASIC 认证(基本认证)的步骤
      2、BASIC 认证的的缺点
      3、Java + SpringBoot 实现 BASIC 认证的Demo
      4、测试
      5、注意事项
      6、Java + SpringBoot + 自定义注解 + 拦截器实现 BASIC 认证

    1、BASIC 认证(基本认证)的步骤    <-- 返回目录

      BASIC 认证(基本认证)是从HTTP/1. 1 就定义的认证方式,是Web服务器与通信客户端之间进行的认证方式。

      BASIC 认证的步骤:

      步骤1:当请求的资源需要 BASIC 认证时,服务器会随状态码 401 Authorization Required返回 WWW-Authenticate 首部字段(响应头)的响应。该字段内包含认证的方式(BASIC)及Request-URI 安全域字符串(realm)

      步骤2:接收到状态码 401 的客户端为了通过 BASIC 认证,需要将用户ID及密码发送给服务器。发送的字符串内容是由用户名 ID 和密码构成,两者中间以冒号(:)连接后,再经过 Base64 编码处理。

      假设用户 ID 为 guest,密码是 guest,连接起来形成 "guest:guest" 这样的字符串。然后经过 Base64 编码成 Z3Vlc3Q6Z3Vlc3Q=。把这个字符串前面拼接 Baisc 形成 "Basic Z3Vlc3Q6Z3Vlc3Q=",然后把这个字符串写入首部字段 Authorization 后,发送请求。

      当用户端为浏览器时,一个仅需要输入用户 ID 和密码即可,浏览器会自动完成 Base64 编码的工作,并再前面添加 "Basic" 前缀。

       步骤3:接收到包含首部字段 Authorization 请求的服务器,会对认证信息的正确性进行验证。如果验证通过,则返回一条包含 Request-URI 资源的响应。

    2、BASIC 认证的的缺点    <-- 返回目录

      1)BASIC 认证虽然采用 Base64 密码方式,但这不是加密处理。不需要任何附加信息即可对其进行解码。换言之,由于明文解码后就是用户名 ID 和密码,再 HTTP 等非加密通信线路上进行 BASIC 认证的过程中,如果被人窃听,被盗的可能性极高。

      所以,BASIC 认证需要配合HTTPS来保证信息传输的安全。

      2)即使密码被强加密,第三方仍可通过加密后的用户名和密码进行重放攻击

      3)如果想再进行一次 BASIC 认证时,一般的浏览器却无法实现认证注销操作。所以,BASIC 认证使用上不够灵活,且达不到多数 Web 网站期望的安全性等级,因此并不常用。

    3、Java + SpringBoot 实现 BASIC 认证的Demo    <-- 返回目录

        application.properties

    server.port=8089
    server.servlet.context-path=/BootDemo

      IndexController

    package com.oy.controller;
    
    import java.util.Base64;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    /**
     * @author oy
     * @version 1.0
     * @date 2020年3月30日
     * @time 下午1:37:36
     */
    @Controller
    public class IndexController {
    
        private static final Base64.Decoder decoder = Base64.getDecoder();
        // private static final Base64.Encoder encoder = Base64.getEncoder();
    
        @RequestMapping("/login")
        @ResponseBody
        public String login(HttpServletRequest req, HttpServletResponse res) {
            if (!isAuth(req, res)) {
                return "{code: 401, msg: "no auth"}";
            }
            
            return "{code: 0, data: {username:"test"}}";
        }
        
        @RequestMapping("/index")
        @ResponseBody
        public String index(HttpServletRequest req, HttpServletResponse res) {
            if (!isAuth(req, res)) {
                return "{code: 401, msg: "no auth"}";
            }
            
            return "{code: 0, data: {xxx:"xxx"}}";
        }
    
        private boolean isAuth(HttpServletRequest req, HttpServletResponse res) {
            String base6AuthStr = req.getHeader("Authorization");
            System.out.println("base6AuthStr=" + base6AuthStr); // base6AuthStr=Basic YWFhOmFhYQ==
            if (base6AuthStr == null) {
                res.setStatus(401);
                res.addHeader("WWW-Authenticate", "basic realm="no auth"");
                return false;
            }
    
            String authStr = new String(decoder.decode(base6AuthStr.substring(6).getBytes()));
            System.out.println("authStr=" + authStr); // authStr=xxx:xxx
    
            String[] arr = authStr.split(":");
            if (arr != null && arr.length == 2) {
                String username = arr[0];
                String password = arr[1];
                // 校验用户名和密码
                if ("test".equals(username) && "123456".equals(password)) {
                    return true;
                }
            }
            // 用户名 ID 和密码校验失败后,重新返回 401 和 WWW-Authenticate 响应头,从而可以重复质询
            res.setStatus(401);
            res.addHeader("WWW-Authenticate", "basic realm="no auth"");
            return false;
        }
    
    }

    4、测试    <-- 返回目录

       浏览器输入:http://localhost:8089/BootDemo/login,弹出下面的对话框,如果输入错误会再次弹出该对话框

       点击取消,查看这次请求的信息,发现服务器响应 401,和 WWW-Authenticate 响应头,告知浏览器需要 BASIC 认证,浏览器收到响应后弹出认证对话框。

         用户名 ID 和密码输入正确,结果(两次请求,第二次浏览器发送包含认证信息的请求头 Authorization)

       认证成功后,访问其他资源,浏览器自动添加 Authorization 请求头

    5、注意事项    <-- 返回目录

      1)发送 WWW-Authenticate 响应头时,basic 是告诉浏览器使用 BASIC 认证,浏览器收到后弹出认证对话框;

      realm表示Web服务器中受保护文档的安全域(比如公司财务信息域和公司员工信息域),用来指示需要哪个域的用户名和密码,最好用" "包括起来。

    res.addHeader("WWW-Authenticate", "basic realm="no auth"");

      2)用户名 ID 和密码校验失败后,重新返回 401 和 WWW-Authenticate 响应头,从而可以重复质询(用户名和密码输入错误,重新弹出输入框)

    6、Java + SpringBoot + 自定义注解 + 拦截器实现 BASIC 认证    <-- 返回目录

     

       application.properties

    server.port=8089
    server.servlet.context-path=/BootDemo

      自定义注解 RequireAuth

    package com.oy.interceptor;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    // can be used to method
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface RequireAuth {
        
    }

      配置 RequireAuth 注解使用的拦截器 RequireAuthInterceptor

    package com.oy.interceptor;
    
    import java.util.Base64;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.springframework.web.method.HandlerMethod;
    import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
    
    public class RequireAuthInterceptor extends HandlerInterceptorAdapter {
    
        final Base64.Decoder decoder = Base64.getDecoder();
        // final Base64.Encoder encoder = Base64.getEncoder();
    
        @Override
        public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) throws Exception {
            // 请求目标为 method of controller,需要进行验证
            if (handler instanceof HandlerMethod) {
                HandlerMethod handlerMethod = (HandlerMethod) handler;
                Object object = handlerMethod.getMethodAnnotation(RequireAuth.class);
    
                /* 方法没有 @RequireAuth 注解, 放行 */
                if (object == null) {
                    return true; // 放行
                }
    
                /* 方法有 @RequireAuth 注解,需要拦截校验 */
                // 没有 Authorization 请求头,或者 Authorization 认证信息不通过,拦截
                if (!isAuth(req, res)) {
                    return false; // 拦截
                }
    
                // 验证通过,放行
                return true;
            }
    
            // 请求目标不是 mehod of controller, 放行
            return true;
        }
    
        private boolean isAuth(HttpServletRequest req, HttpServletResponse res) {
            String base6AuthStr = req.getHeader("Authorization");
            System.out.println("base6AuthStr=" + base6AuthStr); // base6AuthStr=Basic YWFhOmFhYQ==
            if (base6AuthStr == null) {
                res.setStatus(401);
                res.addHeader("WWW-Authenticate", "basic realm="no auth"");
                return false;
            }
    
            String authStr = new String(decoder.decode(base6AuthStr.substring(6).getBytes()));
            System.out.println("authStr=" + authStr); // authStr=xxx:xxx
    
            String[] arr = authStr.split(":");
            if (arr != null && arr.length == 2) {
                String username = arr[0];
                String password = arr[1];
                // 校验用户名和密码
                if ("test".equals(username) && "123456".equals(password)) {
                    return true;
                }
            }
    
            res.setStatus(401);
            res.addHeader("WWW-Authenticate", "basic realm="no auth"");
            return false;
        }
    
    }
    View Code

      SpringBoot 配置类WebConfig:注册拦截器

    package com.oy;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    import com.oy.interceptor.RequireAuthInterceptor;
    
    @Configuration
    public class WebConfig implements WebMvcConfigurer {
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            RequireAuthInterceptor requireAuthInterceptor = new RequireAuthInterceptor();
            registry.addInterceptor(requireAuthInterceptor);
        }
        
    }
    View Code

      测试类,再需要校验用户的方法上面加 @RequireAuth 注解

    package com.oy.controller;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import com.oy.interceptor.RequireAuth;
    
    @Controller
    public class IndexController {
    
        @RequireAuth
        @RequestMapping("/login")
        @ResponseBody
        public String login(HttpServletRequest req, HttpServletResponse res) {
            return "{code: 0, data: {username:"test"}}";
        }
    
        @RequireAuth
        @RequestMapping("/index")
        @ResponseBody
        public String index(HttpServletRequest req, HttpServletResponse res) {
            return "{code: 0, data: {xxx:"xxx"}}";
        }
    
    }

    参考:

      1)《图解HTTP》

  • 相关阅读:
    win10 免费屏幕录像工具下载
    普通人如何在“元宇宙”中获取红利?
    CMake语法—函数(定义&调用)
    CMake语法—普通变量与子目录(Normal Variable And Subdirectory)
    CMake语法—函数(解析参数 PARSE_ARGV)
    算法set_intersection、set_union、set_difference
    CMake语法—缓存变量(Cache Variable)
    C++ 求时差
    CMake语法—环境变量(Environment Variable)
    CMake语法—普通变量与函数(Normal Variable And Function)
  • 原文地址:https://www.cnblogs.com/xy-ouyang/p/12600055.html
Copyright © 2020-2023  润新知