Domino 9 und frühere Versionen > ND9: Entwicklung

Authentifizierter Zugriff auf REST api aus fremder Domain

(1/1)

HH:
Hallo zusammen,

von einer nicht Domino Web Site möchte ich Daten via REST api aus unserer Domino Welt abrufen. Da die Daten benutzerspezifisch sind (Leser-Felder), benötige ich einen authentifizierten Zugriff. Mit viel hin und her, habe ich das auch einigermaßen hinbekommen.

Auszug aus der html Datei in der externen Domain:


--- Code: ---$( document ).ready( function() {

var userName = "xHubert Humpert/Hubi" ;
var password = "paderborn" ;

queryData = function( serviceUrl, requestParams ) {
var modul = "doGet" ;
try {
// check params
if( isEmpty( serviceUrl ) ) {
throw new Error( "missing or empty param serviceUrl" ) ;
}
if( isEmpty( requestParams ) ) {
throw new Error( "missing or empty param requestParams" ) ;
}

$.ajax({
    type: "POST",
    url: serviceUrl ,
    data: JSON.stringify( requestParams ) ,
    contentType: "application/json" ,
    crossDomain : true ,
    dataType: "json",
  beforeSend: function( xhr ) {
    xhr.setRequestHeader('Authorization', 'Basic ' + btoa(unescape( encodeURIComponent( userName + ':' + password ) ) ) ) ;
  },
    success: function( result ) {
    console.log( "success" ) ;
    console.log( result ) ;
    },
    failure: function( result ) {
    console.log( "failure" ) ;
        console.log( result ) ;
    }
});

} catch (e) {
console.log( e ) ;
}
}

isEmpty = function( param ) {
var result = false ;
try {
if( param === undefined || param === null || param === "" ) {
result = true ;
}
} catch ( e ) {
console.log( e ) ;
}
return result ;
}


$( "#sendRequest" ).on( "click", function() {
var serviceUrl = "http://dev01/RESTAuth.nsf/api.xsp/Test" ;
var requestParams = {
action : "getTestData"
}
queryData( serviceUrl, requestParams ) ;
}) ;

}) ;

--- Ende Code ---
Wie man erkennen mag, sende ich die Daten zur Basic Authentification in meinem ajax request mit.

Die xpage, die den Aufruf entgegen nimmt und dann an die service bean weiterleitet:

--- Code: ---<?xml version="1.0" encoding="UTF-8"?>
<xp:view
xmlns:xp="http://www.ibm.com/xsp/core"
xmlns:xe="http://www.ibm.com/xsp/coreex"
rendered="false">

<xe:restService
id="restService6"
pathInfo="Test">
<xe:this.service>
<xe:customRestService
contentType="application/json"
serviceBean="de.egvpb.termin.Test">
</xe:customRestService>
</xe:this.service>
</xe:restService>

</xp:view>

--- Ende Code ---

Hierzu sei gesagt, dass für Anonymous in der ACL no access mit read public documents gesetzt ist. Für die xpage api.xsp ist public access erlaubt.

Und schließlich noch die Service Bean:

--- Code: ---package de.egvpb.termin;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList ;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;

import com.ibm.commons.util.StringUtil;
import com.ibm.domino.services.ServiceException;
import com.ibm.domino.services.rest.RestServiceEngine;
import com.ibm.xsp.extlib.component.rest.CustomService;
import com.ibm.xsp.extlib.component.rest.CustomServiceBean;
import com.ibm.xsp.model.domino.DominoUtils;

public class Test extends CustomServiceBean {

    public void renderService(CustomService service, RestServiceEngine engine) throws ServiceException {
        try {
        HttpServletRequest request = engine.getHttpRequest() ;
            HttpServletResponse response = engine.getHttpResponse();
           
            response.setHeader("Access-Control-Allow-Origin", "*") ;
        response.setHeader("Access-Control-Allow-Methods", "OPTIONS, POST" ) ;
        response.setHeader("Access-Control-Allow-Headers", "Content-Type, authorization" ) ;
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", -1);
            response.setContentType( "application/json" ) ;
           

        String method = request.getMethod() ;
        System.out.println( "Test.renderService, method = " + method );
            if (method.equals("OPTIONS")) {
            // wird bei Authentifizierung aufgerufen
                response.getWriter().write( "options durchlaufen" );
                response.getWriter().close();
            } else if (method.equals("POST")) {
                this.doPost(request, response);
            }
        } catch ( Exception e ) {
        e.printStackTrace() ;
        }
    }

private void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
    try {
    String result = null ;
        String postedData = this.convertStreamToString( request.getInputStream() ) ;
            JSONObject jsonIn = (JSONObject) new JSONParser().parse( postedData ) ;
           
            String action = (String) jsonIn.get("action") ;
            if( action.equals("getTestData") ) {
        result = this.getTestData( request ) ;
            } else {
            String errorMessage = "Test.doPost, undefined param action = " + action ;
            result = this.getParamErrorMessageString(errorMessage) ;
            }
           
            response.getWriter().write( result );
            response.getWriter().close();
} catch (Exception e) {
        e.printStackTrace() ;
}
    }
 
@SuppressWarnings("unchecked")
private String getTestData( HttpServletRequest request ) {
JSONObject result = new JSONObject() ;
try {
result.put( "isError", false ) ;
result.put( "message", "result from rest service, user = " + DominoUtils.getCurrentSession().getEffectiveUserName() ) ;
} catch (Exception e) {
result.put( "isError", true ) ;
result.put( "errorMessage", "Error in REST service" ) ;
        e.printStackTrace() ;
}
return result.toJSONString() ;
}

/**
* Helper functions to read URL parameters
*/
@SuppressWarnings("unused")
private static Boolean getParameterAsBoolean(HttpServletRequest request, String parameter, boolean defaultValue) {
Boolean result = null ;
try {
result = Boolean.valueOf(getParameter(request, parameter, String.valueOf(defaultValue)));
} catch (Exception e) {
        e.printStackTrace() ;
}
return result ;
}

private static String getParameter(HttpServletRequest request, String parameter, String defaultValue) {
String result = "" ;
try {
String pm = request.getParameter(parameter);
if (StringUtil.isEmpty(pm)) {
return defaultValue;
}
result = pm;
} catch (Exception e) {
        e.printStackTrace() ;
}
return result ;
}

@SuppressWarnings("unused")
private static Integer getParameterAsInt(HttpServletRequest request, String parameter, int defaultValue) {
Integer result = 0 ;
try {
String pm = request.getParameter(parameter);
if (StringUtil.isEmpty(pm)) {
return defaultValue;
}
result = Integer.valueOf(pm);
} catch (Exception e) {
        e.printStackTrace() ;
}
return result ;
}

    private String convertStreamToString(InputStream stream) throws IOException {
    String result = "" ;
    try {
        InputStreamReader inputStreamReader = new InputStreamReader( stream, "UTF8" ) ; // Param UTF8 für korrekte Umlaute!!!

        BufferedReader reader = new BufferedReader( inputStreamReader );
            StringBuffer buffer = new StringBuffer();
            String line = "";
            while( (line = reader.readLine()) != null ){
              buffer.append(line);
            }
            reader.close();
            result = buffer.toString() ;
} catch (Exception e) {
        e.printStackTrace() ;
}
return result ;
    }
   
    /**
     * Fehler protokollieren und als JSON String zurückgeben
     * @param errorMessage
     * @return
     */
    @SuppressWarnings("unchecked")
private String getParamErrorMessageString( String errorMessage ) {
    String result = "" ;
    try {
    JSONObject jResult = new JSONObject() ;
    jResult.put( "isError", true ) ;
    jResult.put( "errorMessage", errorMessage ) ;
    result = jResult.toJSONString() ;
} catch (Exception e) {
        e.printStackTrace() ;
}
return result ;
    }
}


--- Ende Code ---

Falls die Credentials korrekt sind, wird mit der success function des ajax requests der Name des authentifizierten Benutzers ausgegeben. Falls nicht, erhalte ich auf der Konsole des Browsers die Meldung


--- Code: ---Access to XMLHttpRequest at 'http://dev01/RESTAuth.nsf/api.xsp/Test' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
jquery-3.3.1.min.js:2 POST http://dev01/RESTAuth.nsf/api.xsp/Test net::ERR_FAILED

--- Ende Code ---

Meine Fragen:

* kann / sollte man das so machen?

* wie kann man die CORS-Fehlermeldung durch eine sprechendere Variante ersetzen bzw. vorab eine Prüfung realisieren?
Ich bitte um kritische Würdigung.

Gruß
Hubert

jBubbleBoy:
Eine Erklärung gibt es hier:
https://developer.mozilla.org/de/docs/Web/HTTP/CORS/Errors/CORSFehltQuelleErlauben

Kannst Du denn das Script, zum Laden der Ressourcen mit Anmeldung, in die Notes-DB legen und von der Webseite aus einbinden?

HH:
Die Erklärung war mir schon klar. Deshalb setze ich auch im response header die entsprechenden Felder. Die scheinen allerdings bei einer fehlgeschlagenen Authentifizierung nicht im Client anzukommen.

An die aufrufende Seite möchte ich nur die Rohdaten im JSON Format liefern, damit dort dann die die Daten entsprechend darstellt werden.

Navigation

[0] Themen-Index

Zur normalen Ansicht wechseln