Basic认证方式访问url

时间:2022-09-30 16:21:06

今天做不成的事,明天也不会做好。


同学们,今天我们来了解一下Basic认证。Basic认证在访问WebApi时需要验证账号密码,它是最基础的验证之一。通常我们裸奔一个服务的url,如果被别有用心的人拿到了,那么他就能无限调用接口操作数据库/获取数据,后果可以想象。同时认证可以有效保证数据安全。好了,废话不多说,咋们进入正题。

1.Basic认证介绍

1.1 常见身份验证方式

1.时间戳/密文传递验证
2.通过token验证
3.basic认证
……
无论是哪种方式,都是为了保证url的安全性,这个可以根据密级的提高采用不同的方式,同时还有一些其他的身份认证方式,大家有兴趣可以去研究。

1.2 Bacic认证概述

在HTTP协议进行通信的过程中,HTTP协议定义了基本认证过程以允许HTTP服务器对WEB浏览器进行用户身份证的方法,当一个客户端向HTTP服务 器进行数据请求时,如果客户端未被认证,则HTTP服务器将通过基本认证过程对客户端的用户名及密码进行验证,以决定用户是否合法。客户端在接收到HTTP服务器的身份认证要求后,会提示用户输入用户名及密码,然后将用户名及密码以BASE64加密,加密后的密文将附加于请求信息中, 如当用户名为anjuta,密码为:123456时,客户端将用户名和密码用“:”合并,并将合并后的字符串用BASE64加密为密文,并于每次请求数据 时,将密文附加于请求头(Request Header)中。HTTP服务器在每次收到请求包后,根据协议取得客户端附加的用户信息(BASE64加密的用户名和密码),解开请求包,对用户名及密码进行验证,
如果用 户名及密码正确,则根据客户端请求,返回客户端所需要的数据;否则,返回错误代码或重新要求客户端提供用户名及密码。
--简单说就是,服务器每次会从请求的客户端的请求头中获取"Authorization"这个请求参数里面的密文去验证。客户端请求参数添加如下:将账号密码通过':'拼接成字符串(密码在后),然后通过Base64编码后再给前面拼接'Basic '字符串。服务端获取到这个密文后,通过Base64解码并验证。--

1.3 Basic认证的缺点

HTTP基本认证的目标是提供简单的用户验证功能,其认证过程简单明了,适合于对安全性要求不高的系统或设备中,如大家所用路由器的配置页面的认证,几乎都采取了这种方式。其缺点是没有灵活可靠的认证策略,如无法提供域(domain或realm)认证功能,另外,BASE64的加密强度非常低,可以说仅能防止sohu的搜索把它搜到了。当然,HTTP基本认证系统也可以与SSL或者Kerberos结合,实现安全性能较高(相对)的认证系统

2.Basic认证的客户端实现

2.1 纯ajax实现

        var auth = "Basic " + btoa("BD_CONNECT" + ":" + "xxxx@")

        $.ajax({
            type: "POST",
            url: "http://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
            dataType: 'jsonp',
            async: false,
//            beforeSend:function(req){
//                req.setRequestHeader('Authorization', "Basic QkRfQ09OTkVDVDpiZ3kyMDE4QA==")
//            },
            headers: {
                "Authorization": auth
            },
            data:{"param":"param"},
            success: function (data){
                alert(data);
            }
        });

这里需要注意的有两点:
1.dataType里面的json格式需要换成jsonp的传输协议,以解决跨域的问题。同时不要把json和jsonp混为一谈。
2.auth参数就是经过Base64加密的密文,js自带的btoa()方法作用是把参数进行Base64加密。

实际上我用这个方法访问失败了,貌似时Authorization一直不能放到请求头里面,或者说是不能被解析,反正就是一直要我手动输入账号密码,还希望有懂的大神来为我解析一下,感激不尽。
因此才有了下面的java实现方式!

2.2 JAVA实现

DataOutputStream out = null;
        BufferedReader in = null;
        String result = "";
        try {
            URL realUrl = new URL("http://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
            HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection();
            //conn.setConnectTimeout(5000);
            String plainCredentials = "BD_CONNECT:xxxx@";
            String base64Credentials = new String(Base64.encodeBase64(plainCredentials.getBytes()));
            conn.setRequestProperty("Authorization", "Basic " + base64Credentials);
// conn.setRequestProperty("Content-type", "text/plain");
// conn.setRequestProperty("Origin", "chrome-extension://aejoelaoggembcahagimdiliamlcdmfm");
// conn.setRequestProperty("Accept", "*/*");
// conn.setRequestProperty("connection", "Keep-Alive");
// conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            conn.setDoOutput(true);//需要输出
            conn.setDoInput(true);//需要输入
            conn.setRequestMethod("POST");
            // Post 请求不能使用缓存
            conn.setUseCaches(false);
// conn.connect();

            // 此处getOutputStream会隐含的进行connect(即:如同调用上面的connect()方法,
            // 所以在开发中不调用上述的connect()也可以)。
            OutputStream outStrm = conn.getOutputStream();
            out = new DataOutputStream(outStrm);
            // 向对象输出流写出数据,这些数据将存到内存缓冲区中
            out.writeBytes(param);

            // 刷新对象输出流,将任何字节都写入潜在的流中(些处为ObjectOutputStream)
            out.flush();
            // 关闭流对象。此时,不能再向对象输出流写入任何数据,先前写入的数据存在于内存缓冲区中,
            // 在调用下边的getInputStream()函数时才把准备好的http请求正式发送到服务器
            out.close();

            // 调用HttpURLConnection连接对象的getInputStream()函数,
            // 将内存缓冲区中封装好的完整的HTTP请求电文发送到服务端。
            InputStream inStrm = conn.getInputStream(); // <===注意,实际发送请求的代码段就在这里

            // 上边的httpConn.getInputStream()方法已调用,本次HTTP请求已结束,下边向对象输出流的输出已无意义,
            // 既使对象输出流没有调用close()方法,下边的操作也不会向对象输出流写入任何数据.
            // 因此,要重新发送数据时需要重新创建连接、重新设参数、重新创建流对象、重新写数据、
            // 重新发送数据(至于是否不用重新这些操作需要再研究)
// objOutputStrm.writeObject(new String(""));
// conn.getInputStream();

            in = new BufferedReader(new InputStreamReader(inStrm));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
                in.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        return result;

这一段代码我成功了,这是我从网上整合了多篇文章的代码,因为每篇文章的代码都有缺陷,同时上面的注释都很完整:
1.输出流使用DataOutputStream而不是用ObjectOutputStream,这样解决了传参时后在头部加上乱码的错误。
2.使用完了记得关闭流,你的内存不是无限的。

说到这,Basic认证客户端使用方式就说完了。下面来看看服务端的使用!

3.Basic认证的服务端实现

HttpSession session=request.getSession();
         String user=(String)session.getAttribute("user");
         String pass;
         if(user==null){
             try{
                response.setCharacterEncoding("GBK");
                PrintWriter ut=response.getWriter();
                String authorization=request.getHeader("authorization");
                if(authorization==null||authorization.equals("")){
                    response.setStatus(401);
                    response.setHeader("WWW-authenticate","Basic realm=\"请输入管理员密码\"");
                    out.print("对不起你没有权限!!");
                    return;
                }
                String userAndPass=new String(new BASE64Decoder().decodeBuffer(authorization.split(" ")[1]));
                if(userAndPass.split(":").length<2){
                    response.setStatus(401);
                    response.setHeader("WWW-authenticate","Basic realm=\"请输入管理员密码\"");
                    out.print("对不起你没有权限!!");
                    return;
                }
                user=userAndPass.split(":")[0];
                pass=userAndPass.split(":")[1];
                if(user.equals("111")&&pass.equals("111")){
                    session.setAttribute("user",user);
                    RequestDispatcher dispatcher=request.getRequestDispatcher("index.jsp");
                    dispatcher.forward(request,response);
                }else{
                    response.setStatus(401);
                    response.setHeader("WWW-authenticate","Basic realm=\"请输入管理员密码\"");
                    out.print("对不起你没有权限!!");
                    return;
                }
             }catch(Exception ex){
                ex.printStackTrace();
             }
         }else{
             RequestDispatcher dispatcher=request.getRequestDispatcher("index.jsp");
             dispatcher.forward(request,response);
       }

建议同学们可以把这一段单独抽离出来,可以作为aop的前置增强,每一次执行方法前进行调用。