代码之家  ›  专栏  ›  技术社区  ›  Community wiki

REST应用程序中的基本身份验证

  •  14
  • Community wiki  · 技术社区  · 1 年前

    环境:

    • JAVA
    • 玻璃鱼
    • 不同机器中的REST服务
    • HTML5客户端 AJAX和JQuery
    • 运动衫

    这就是我迄今为止所实现的:

    HTML5客户端

    $('#btnSignIn').click(function () {
        var username = $("#username").val();
        var password = $("#password").val();
    
        function make_base_auth(user, password) {
            var tok = user + ':' + password;
    
            var final = "Basic " + $.base64.encode(tok);
            console.log("FINAL---->" + final);
            alert("FINAL---->" + final);
    
            return final;
        }
    
        $.ajax({
            type: "GET",
            contentType: "application/json",
            url: "http://localhost:8080/SesameService/webresources/users/secured/login",
            crossDomain: true,
            dataType: "text",
            async: false,
            data: {},
            beforeSend: function (xhr) {
                xhr.setRequestHeader('authorization', make_base_auth(username, password));
            },
            success: function () {
                alert('Thanks for your signin in! ');
    
            },
            error: function (jqXHR, textStatus, errorThrown) {
                console.log(textStatus, errorThrown);
                alert(' Error in signIn-process!! ' + textStatus);
            }
        });
    });
    

    服务器

    在安全中,我没有启用安全管理器,它已被禁用!

    我已经为Glassfish配置了BASIC身份验证,我的web.xml如下所示:

    <servlet-mapping>
        <servlet-name>ServletAdaptor</servlet-name>
        <url-pattern>/webresources/*</url-pattern>
    </servlet-mapping>
    
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>REST Protected resources</web-resource-name>
            <description/>
            <url-pattern>/users/*</url-pattern>
            
        </web-resource-collection>
        <auth-constraint>
            <role-name>admin</role-name>
            <role-name>customer</role-name>
            <role-name>user</role-name>
        </auth-constraint>
    </security-constraint>
    
    
    
    <login-config>
            <auth-method>BASIC</auth-method>
            <realm-name>jdbcRealm</realm-name>
        </login-config>
        <security-role>
            <role-name>admin</role-name>
        </security-role>
        <security-role>
            <role-name>user</role-name>
        </security-role>
        <security-role>
            <description/>
            <role-name>customer</role-name>
        </security-role>
    

    玻璃鱼

    enter image description here

    日志

    FINE: [Web-Security] Setting Policy Context ID: old = null ctxID = SesameService/SesameService
    FINE: [Web-Security] hasUserDataPermission perm: ("javax.security.jacc.WebUserDataPermission" "/webresources/users/secured/login" "GET")
    FINE: [Web-Security] hasUserDataPermission isGranted: true
    FINE: [Web-Security] Policy Context ID was: SesameService/SesameService
    FINE: [Web-Security] hasResource isGranted: true
    FINE: [Web-Security] hasResource perm: ("javax.security.jacc.WebResourcePermission" "/webresources/users/secured/login" "GET")
    

    问题:

    1. 如果我在用户注册时在客户端加密(而不是编码)密码,并在SSL/HTTPS下传输,这是实现这一点的安全和好方法吗?

    2. 如果我在没有客户端的情况下使用REST服务,它总是打开的,为什么?没有BASIC身份验证?我理解那些url模式有什么问题吗?

       http://localhost:8080/SesameService/webresources/users/secured/login
      
    3. 如果我能做到这一点,如何测试它,因为现在如果我认证一次,我总是被授权的?是否可以在REST服务中以程序方式“注销”,或者通常如何实现“注销”?

    4. 当在头中使用带有强制性base64编码用户名的Authorization时:password我是否也必须将我的用户名和密码编码到DB?我尝试了一下,并将Encoding(允许的值是Hex和Base64)添加到jdbcRealm到Glassfish,似乎密码就足够了,但当两者都在客户端中编码时会发生什么?

    更新: 我更改了web.xml,现在直接在浏览器中调用REST服务时,BASIC身份验证正在工作: http://localhost:8080/SesameService/users/secured/login

    变化:

    • 我在Glassfish启用了安全管理器
    • 我更改了url模式
        <servlet-mapping>
            <servlet-name>ServletAdaptor</servlet-name>
            <url-pattern>/*</url-pattern>----> I took webresources off. It was generated by Netbeans
        </servlet-mapping>
    
    • 我将服务的url更改为: http://localhost:8080/SesameService/users/secured/login

    现在,我在尝试从HTML5客户端进行身份验证时收到了HTTP/1.1 401未经授权。

    请求标头:

    Origin: http://localhost:8383
    Host:localhost:8080
    Connection:keep-alive
    Access-Control-Request-Method:GET
    Access-Control-Request-Headers:authorization,content-type
    

    回应:

    x-powered-by:Servlet/3.0 JSP/2.2 (GlassFish Server Open Source Edition 3.1.2.2 Java/Oracle Corporation/1.7)
    WWW-Authenticate:Basic realm="jdbcRealm"
    Server:GlassFish Server Open Source Edition 3.1.2.2
    Pragma:No-cache
    Expires:Thu, 01 Jan 1970 02:00:00 EET
    Date:Sat, 13 Apr 2013 15:25:06 GMT
    Content-Type:text/html
    Content-Length:1073
    Cache-Control:no-cache
    

    更新2

    当我尝试使用JavaScript+Authorization头进行身份验证时,我得到了401错误,并且在日志中:

    FINE: [Web-Security] Setting Policy Context ID: old = null ctxID = SesameService/SesameService
    FINE: [Web-Security] hasUserDataPermission perm: ("javax.security.jacc.WebUserDataPermission" "/users/secured/login" "OPTIONS")
    FINE: [Web-Security] hasUserDataPermission isGranted: true---->!!!!!!!!!!!!!
    FINE: [Web-Security] Policy Context ID was: SesameService/SesameService
    FINE: [Web-Security] Codesource with Web URL: file:/SesameService/SesameService
    FINE: [Web-Security] Checking Web Permission with Principals : null------->!!!!!!!
    FINE: [Web-Security] Web Permission = ("javax.security.jacc.WebResourcePermission" "/users/secured/login" "OPTIONS")
    FINEST: JACC Policy Provider: PolicyWrapper.implies, context (SesameService/SesameService)- result was(false) permission (("javax.security.jacc.WebResourcePermission" "/users/secured/login" "OPTIONS"))
    FINE: [Web-Security] hasResource isGranted: false------->!!!!!!!!!
    FINE: [Web-Security] hasResource perm: ("javax.security.jacc.WebResourcePermission" "/users/secured/login" "OPTIONS")
    FINEST: JACC Policy Provider: PolicyWrapper.getPermissions(cs), context (null) codesource ((null <no signer certificates>)) permissions: java.security.Permissions@5d4de3b0 (
    

    更新3 我不能成为第一个也是唯一一个试图在跨域情况下使用BASIC进行身份验证的人。 我这样更改了交叉原点过滤器:

    response.getHttpHeaders().putSingle("Access-Control-Allow-Headers", "Authorization");
    

    NO 401错误不再存在,但JavaScript中仍然存在错误。在玻璃鱼日志中:

    FINEST: JACC Policy Provider:
    getPolicy (SesameService/SesameService) is NOT in service----->!!!!!!!!
    FINE: JACC Policy Provider: file arrival check type: granted  arrived: false exists: false lastModified: 0 storedTime: 1365968416000 state: deleted SesameService/SesameService
    FINE: JACC Policy Provider: file arrival check type: excluded  arrived: false exists: false lastModified: 0 storedTime: 0 state: deleted SesameService/SesameService
    FINE: TM: getTransaction: tx=null, tm=null
    FINE: TM: componentDestroyedorg.apache.catalina.servlets.DefaultServlet@227fe9a8
    FINE: TM: resourceTable before: 0
    FINE: TM: resourceTable after: 0
    

    顺便说一句,因为我从来没有做过这项工作,所以做这项工作的方式与直接在自己的域中调用REST服务的方式相同。那么,首先打开客户端请求、服务器请求和用户名密码窗口,然后客户端请求和服务器验证并响应页面?我正在尝试获得它:带有Authorization头的请求,来自服务器的响应和来自rest服务的结果,就这样。知道如何保护rest服务吗?比这容易吗?这是不可能的。

    更新4

    我只是试图将我的HTML5客户端转移到java web项目下,只有纯html页面,在同一域下,BASIC身份验证100%有效。所以原因是因为跨领域的环境。

    2 回复  |  直到 3 年前
        1
  •  10
  •   Stann0rz    11 年前

    如果我在用户注册时在客户端加密(而不是编码)密码,并在SSL/HTTPS下传输,这是实现这一点的安全和好方法吗?

    很可能您应该简单地通过SSL/HTTPS以纯文本形式传递密码。

    SSL/HTTPS中的所有通信都是加密的,因此加密密码可能不是一个好主意,除非您需要确保web服务器(从技术上讲是HTTPS终止器)看不到密码。

    如果我在没有客户端的情况下使用REST服务,它总是打开的,为什么?没有BASIC身份验证?我理解那些url模式有什么问题吗?

    不确定我是否理解这个问题。然而,BASIC身份验证不是REST中身份验证的好模式,因为它以纯文本形式传递密码。

    如果我能做到这一点,如何测试它,因为现在如果我认证一次,我总是被授权的?是否可以在REST服务中以程序方式“注销”,或者通常如何实现“注销”?

    在基本身份验证中,用户名和密码由客户端传递 在每个HTTP请求中 。如果客户端没有传递凭据,则服务器会拒绝该请求。因此,没有会话的概念。

    然而,就Java EE服务器而言,登录会创建一个用户会话,同一用户将来的请求将使用同一会话。如果您这样配置它,则此会话将超时。

    如果注销很重要(即控制用户会话),那么您必须为此创建一个servlet(/logoout),从而使HTTP会话无效。

    标准Java安全模型的工作原理如下:当用户登录到安全领域时,Java EE服务器会在浏览器中存储一个安全cookie。浏览器在向同一领域发出的每个请求中都会将此cookie发送回Java EE服务器。Java EE服务器在每个请求中检查这个cookie,并使用它来识别用户,将请求连接到用户的会话。

    因此,您可能希望REST服务与web应用程序处于相同的安全领域,以便浏览器和服务器无缝工作。

    当在头中使用带有强制性base64编码用户名的Authorization时:password我是否也必须将我的用户名和密码编码到DB?我尝试了一下,并将Encoding(允许的值是Hex和Base64)添加到jdbcRealm到Glassfish,似乎密码就足够了,但当两者都在客户端中编码时会发生什么?

    不,不要对用户名或密码进行编码——这一切都是在浏览器和Glassfish中深入处理的。如果您在客户端中进行编码,客户端将对编码进行编码,服务器将拒绝密码。


    你能告诉我,是否可以将html5页面中的j_security_check与javaScript一起使用,或者我是否又遇到了问题:)我已经制作了几个Primefaces+jsf应用程序,在那里我使用了FORM auth,没有任何问题,但这对我来说完全是一场灾难。

    假设RESTful服务位于同一域和安全领域(然后登录到web应用程序将允许浏览器向RESTURI发送正确的cookie),那么您应该能够使用j_security_check轻松地实现这一点。

    但是,请注意,其他应用程序在访问REST服务时会遇到困难。基本上,他们必须通过j_security_check登录,然后维护Glassfish发送的cookie。如果您确实需要其他应用程序以编程方式访问这些服务,那么您将需要另一个解决方案:

    1. 您可以设置安全领域,以允许不同的身份验证程序“足够”;设置HTTP BASIC Auth 并确保没有标记为“必要”
    2. 部署RESTful服务两次,另一次是不同的URI。如果您想使用HTTP BASIC Auth,这可能是SSL/HTTPS的终点,以确保密码得到安全处理
        2
  •  0
  •   peterfoldi    7 年前



    我正在使用Spring安全性开发Spring MVC框架,并使用基本的身份验证:

    基本上,在HTTP基本身份验证中,用户名和密码在Base64类(来自util包)的帮助下转换为密钥或访问令牌。

    该密钥被设置到HTTP URL的头中,然后被发送到服务器。

    它在服务器上运行良好:通过相同的Base64解码用户名密码密钥,然后与数据库用户名和密码匹配。如果验证完成,则进行进一步处理。

    这保证了API URL的安全性。每个身体都不容易接近。

    代码: 创建访问令牌(密钥)代码为:

    注意:我们在Base64中传递字符串(来自util包)“YourUsername:YourPassword”进行编码。
    是的,用户名和密码是“:”分隔的。

            String encodeddata = new String(Base64.encodeBase64("username:password".getBytes()));
            System.out.println("encodedBytes " + encodeddata);
    

    将密钥设置到标头的代码:

    //使用密钥请求URL

            HttpClient client = HttpClientBuilder.create().build();
            HttpGet get = new HttpGet("http://google.com/Abc/api/vou/redeemed");
            get.setHeader("Authorization", "Basic "+encodeddata); //key getting above
    

    注意:您可以将其与Get和Post方法一起使用。这里我只使用get方法。 //响应

            HttpResponse jresponse = client.execute(get);
    
            System.out.println("Response Code : "+ jresponse.getStatusLine().getStatusCode());
    
            BufferedReader rd = new BufferedReader(new InputStreamReader(jresponse.getEntity().getContent()));
    
            StringBuffer result = new StringBuffer();
            String line = "";
            while ((line = rd.readLine()) != null) {
                result.append(line);
            }
    
            System.out.println("------Response is ::: "+ result);
    

    //DECODE键

            String usernameAndPassword = null;
            try {
                usernameAndPassword = new String(Base64.getDecoder().decode(encodedUserPassword));
            } catch (Exception e) {
                System.out.println("SERVER_ERROR");
            }
            System.out.println(usernameAndPassword);
    
            final StringTokenizer tokenizer = new StringTokenizer(encodedUserPassword, ":");
            final String username = tokenizer.nextToken();
            final String password = tokenizer.nextToken();
    
            System.out.println(username);
            System.out.println(password);