14
votes

Environment:

  • JAVA
  • Glassfish
  • REST-services in different machine
  • HTML5-client with AJAX and JQuery
  • Jersey

This is what I have implemented so far:

HTML5-client

$('#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);
        }
    });
});

SERVER

In Security, I haven't got Security Manager enabled, it is disabled!

I have configured BASIC-authentication to Glassfish and my web.xml looks like that:

<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>

GLASSFISH

enter image description here

LOG

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")

QUESTION:

  1. If I encrypt (NOT encode) password in client when user is signing up and transfer it under the SSL/HTTPS, is this secure and good way to implement this?

  2. If I use REST-service without client, it is always open, WHY? No BASIC-authentication? Have I understood something wrong with those url-patterns?

     http://localhost:8080/SesameService/webresources/users/secured/login
    
  3. IF I get this working how to test that, because now if I authenticate once, I am authorised always? Is it possible to "log out" programatically inside the REST-service or in generally how to implement Log out?

  4. When using Authorization in header with mandatory base64-encoded username:password do I have to encode my username and password to DB as well? I tried that and added Encoding (allowed values are Hex and Base64) to jdbcRealm to Glassfish and it seems that password is enough, but what happens when both are encoded in client?

UPDATE: I changed web.xml and now BASIC-authentication is working when calling REST-service straight in browser :http://localhost:8080/SesameService/users/secured/login

Changes:

  • I enabled security manager in Glassfish
  • I changed url-pattern
    <servlet-mapping>
        <servlet-name>ServletAdaptor</servlet-name>
        <url-pattern>/*</url-pattern>----> I took webresources off. It was generated by Netbeans
    </servlet-mapping>
  • I changed the url to service to this: http://localhost:8080/SesameService/users/secured/login

Now I get a HTTP/1.1 401 Unauthorized when trying to authenticate from HTML5-client.

Request headers:

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

Response:

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

UPDATE 2

When I try to authenticate with JavaScript + Authorization-header I got 401 error and that in the log:

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 (

UPDATE 3 I can't be the first and only person who is trying to authenticate using BASIC in cross domain case. I changed my cross origin filters like that:

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

NO 401 error anymore but still error in JavaScript. IN Glassfish log:

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

BTW, because I have never get this work, does this work same way than calling the REST-service direct in its own domain. So, First client requests, server requests and username-password window opens, then client request and server authenticate and response the page? I am trying to get it: Request with Authorization header in it, response from server with result from the rest service and that's it. Any idea how to secure REST-services? Easier than that? This is impossible.

UPDATE 4

I just tried to move my HTML5-client to under java web-project, just pure html-pages and under same domain and BASIC-authentication is working 100 %. So the reason is because of cross-domain environment.

2
Have you tried closing and re-opening your browser? With BASIC auth, the browser caches the credentials until you close it - it won't constantly prompt you over and over. And no - there's no way to log out with BASIC because the credentials are sent with every single request. - Colselaw
I am using Private/ignognito-winwows in Chrome and Firefox. I can't even see the Basic-login window in any case, I can get in every time. I think that the problem is in url-patterns, but I don't know why and how. - Sami
I Enabled the security manager and it seems that now it is really using it, BUT I still get in without BASIC-authentication.:FINEST: JDBCRealm : jaas-context= jdbcRealm, datasource-jndi = sesame, db-user = null, digest-algorithm = none, encoding = null, charset = null INFO: SEC1115: Realm [jdbcRealm] of classtype [com.sun.enterprise.security.auth.realm.jdbc.JDBCRealm] successfully created. FINE: Configured realm: jdbcRealm FINE: Default realm is set to: jdbcRealm INFO: SEC1011: Security Service(s) Started Successfully FINE: Policy already installed. Will not re-install. - Sami
Putting your updates at the top of your question makes it hard (impossible?) to understand what's going on. I don't even know where your original question begins. - Ryan Stewart

2 Answers

9
votes

If I encrypt (NOT encode) password in client when user is signing up and transfer it under the SSL/HTTPS, is this secure and good way to implement this?

Most probably you should simply pass the password in plain text over SSL/HTTPS.

All communication in SSL/HTTPS is encrypted, so it's probably not a good idea to encrypt the password as well unless you need to ensure the web server (technically the HTTPS terminator) cannot see the password.

If I use REST-service without client, it is always open, WHY? No BASIC-authentication? Have I understood something wrong with those url-patterns?

Not sure I understand the question. However BASIC auth is not a good pattern for authentication in REST because it is passing the password in plain text.

IF I get this working how to test that, because now if I authenticate once, I am authorised always? Is it possible to "log out" programatically inside the REST-service or in generally how to implement Log out?

In Basic Auth, the username and password are passed by the client in every HTTP request. If the credentials are not passed by the client, then the server rejects the request. As such there is no concept of session.

However, as far as the Java EE server is concerned, the login creates a user session and future requests by the same user will use the same session. If you so configure it, this session will time out.

If logging out is important (i.e. control user sessions), then you have to create a servlet (/logout) for this which invalidates the HTTP session.

The standard Java security model works as follows: When the user logs in to a security realm, the Java EE server stores a secure cookie in your browser. The browser sends this cookie back to the Java EE server in each request to the same realm. The Java EE server checks for this cookie in every request and uses it to identify the user, connecting the request to the user's session.

So you probably want to do have the REST service in the same security realm as the web application, so the browser and server work seamlessly.

When using Authorization in header with mandatory base64-encoded username:password do I have to encode my username and password to DB as well? I tried that and added Encoding (allowed values are Hex and Base64) to jdbcRealm to Glassfish and it seems that password is enough, but what happens when both are encoded in client?

No, don't encode the username or password - this is all handled deep within the browser and Glassfish. If you encode in the client, the client will encode the encoding and the server will reject the password.


Could you tell me if is it possible to use j_security_check from html5-page with javaScript or am I in problems again :) I have made couple of Primefaces + jsf-application where I used FORM-auth and there wasn't any problem, but this has been totally disaster case for me.

You should be able to get this working comfortably with j_security_check assuming that the RESTful services stay in the same domain and security realm (then logging on to the web application will allow the browser to send the correct cookie to the REST URIs).

Do note, however, that other applications will have difficulty accessing the REST services. Basically, they will have to log in via the j_security_check and then maintain the cookies sent by Glassfish. If you do need other applications to access these services programmatically, then you will need another solution:

  1. You can set up the security realm to allow different authenticators to be 'sufficient'; set up HTTP BASIC Auth as well and make sure that none are marked 'necessary'
  2. Deploy the RESTful services twice, the other being a different URI. If you want to use HTTP BASIC Auth, this might be a SSL/HTTPS end point to ensure the passwords are handled securely
0
votes



I am working on Spring MVC framework with spring security and use basic Authentication:

Basically, in HTTP basic authentication the username and password are converted into a key or access token with the help of Base64 class (from util package).

And that key is set into the header of the HTTP URL and then hit to the server.

It works fine and at server: decode the username password key by same Base64 and then match with the database username and password. if authentication done then further process.

This give the safety of API Urls. Every body not access easily.

Code: Create access token (key) code is:

NOTE: we pass string in Base64(from util package) "YourUsername:YourPassword" for encoding.
Yes, username and password are ":" separated.

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

Code for setting key into header:

//REQUEST TO URL WITH KEY

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

Note: You can use this with both Get and Post method. Here I am using only with get method. //RESPONSE

        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 Key

        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);