Autor Thema: Authentifizierter Zugriff auf REST api aus fremder Domain  (Gelesen 2790 mal)

Offline HH

  • Senior Mitglied
  • ****
  • Beiträge: 339
  • Geschlecht: Männlich
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 ) ;
	}) ;
	
}) ;
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>

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


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

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

Offline jBubbleBoy

  • Gold Platin u.s.w. member:)
  • *****
  • Beiträge: 1.276
  • Geschlecht: Männlich
Re: Authentifizierter Zugriff auf REST api aus fremder Domain
« Antwort #1 am: 19.02.20 - 14:47:55 »
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?
Gruss Erik :: Freelancer :: Notes, Java, Web, VBA und DomNav 2.5 / NSE 0.16
--
Nur ein toter Bug, ist ein guter Bug!

Offline HH

  • Senior Mitglied
  • ****
  • Beiträge: 339
  • Geschlecht: Männlich
Re: Authentifizierter Zugriff auf REST api aus fremder Domain
« Antwort #2 am: 19.02.20 - 15:01:19 »
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.

 

Impressum Atnotes.de  -  Powered by Syslords Solutions  -  Datenschutz