cancel
Showing results for 
Search instead for 
Did you mean: 
Read only

Single-Sign-On (SSO) configuration on JAVA Stack through HTTP Header method

Former Member
0 Likes
4,539

Hello SDN community,

in the context of a Proof of Concept, we are testing the integration of Microsoft Sharepoint Portal with SAP Backend (addin) systems.

As the architecture impose use an external scenario (access from the internet), we couldn't use the Kerberos (SPNego) solution and thus we chosed the http header solution which in short uses an intermediary web server (in this case the IIS of the MOSS solution) which will act as authority.

I miss information on how the workflow works for this http header authentication method. Through the visual administrator of the addin JAVA stack, it is possible to configure each application with a customized authentication (a choice of security modules). But this all that I know.

My task is to configure SSO. From a sharepoint portal, the user should be able to access Web Dynpros and BSPs. I imagine that the very first call to a webdynpro or bsp (or maybe when we log on the sharepoint portal), the request to the WDP or BSP will first be forwareded by the intermediary server to the JAVA stack (or is it the SAP dispatcher that has to be configured).

Is there an application to be built on the java stack to deal with the authentication, modify http header?

What will the Java stack return? a sap long ticket? a token?

How will the redirect work (to by example a BSP which is in the ABAP stack)?

SAP preconise to secure with SSL the link between the intermediary web server and the JAVA stack, is IP restriction also a solution?

A lot of questions about how this SSO http header should work,

I would be very greatful for any help, or info,

Kind regards,

Tanguy Mezzano

View Entire Topic
Former Member
0 Likes

Here's the spec:


if (cookie.isDomainAttributeSpecified() 
152             && (!cookie.getDomain().equals(host))) {
153                 
154             // domain must start with dot
155             if (!cookie.getDomain().startsWith(".")) {
156                 throw new MalformedCookieException("Domain attribute \"" 
157                     + cookie.getDomain() 
158                     + "\" violates RFC 2109: domain must start with a dot");
159             }
160             // domain must have at least one embedded dot
161             int dotIndex = cookie.getDomain().indexOf('.', 1);
162             if (dotIndex < 0 || dotIndex == cookie.getDomain().length() - 1) {
163                 throw new MalformedCookieException("Domain attribute \"" 
164                     + cookie.getDomain() 
165                     + "\" violates RFC 2109: domain must contain an embedded dot");
166             }
167             host = host.toLowerCase();
168             if (!host.endsWith(cookie.getDomain())) {
169                 throw new MalformedCookieException(
170                     "Illegal domain attribute \"" + cookie.getDomain() 
171                     + "\". Domain of origin: \"" + host + "\"");
172             }
173             // host minus domain may not contain any dots
174             String hostWithoutDomain = host.substring(0, host.length() 
175                 - cookie.getDomain().length());
176             if (hostWithoutDomain.indexOf('.') != -1) {
177                 throw new MalformedCookieException("Domain attribute \"" 
178                     + cookie.getDomain() 
179                     + "\" violates RFC 2109: host minus domain may not contain any dots");
180             }
181         }

Former Member
0 Likes

Hi Tanguy,

the dot is absolutely correct for the domain value in the cookie (see RFC 2109)

Here's an extract of RFC 2109

Domain=domain
      Optional.  The Domain attribute specifies the domain for which the
      cookie is valid.  An explicitly specified domain must always start
      with a dot.

As far as I can see you are getting an error messaging regarding the Cookie because of an exception you are throwing yourself??

What I don't get is why are you using the Java Client to connect to Backend? Don't you want your client (Frontend Browser) to perform Single Sign On against your Backend? You could use a Java Servlet Application that serves as an Authenticator Servlet which is being accessed by your client (Browser) and in the Background your Servlet accesses the J2EE using the Header Variable in order to get the Cookie and fetch it from the Server response.

In your client response (the response from your Servlet App to the Frontend) you could send the Cookie to your Client (Browser). Furthermore the response could contain a redirect to your Backend and your client will follow the redirect and actually send the Cookie in order to authenticate via SSO.

This could something like this (Your Servlet):

public class AuthenticatorServlet extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {
   static final long serialVersionUID = 1L;
..

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
  String REMOTE_USER = "";
  String REDIRECT_URL = "";
  try {
	REMOTE_USER = request.getParameter("REMOTE_USER");
	REDIRECT_URL = request.getParameter("redirectURL");
  } catch(Exception Ex) {
	Ex.printStackTrace();
  }
		
  // We get the cookies from the remote request and add them to our client response
  org.apache.commons.httpclient.Cookie[] cookies = HTTPClient.request(REMOTE_USER);
		

  for(int i=0; i<cookies.length; i++) {
    javax.servlet.http.Cookie clientCookie = new   javax.servlet.http.Cookie(cookies<i>.getName(),cookies<i>.getValue());
    // Here you could fetch some additional info from your original cookie or set some parameters with
    // custom values as you desire!
    clientCookie.setDomain(".domain.com");
    clientCookie.setPath("/");
    response.addCookie(clientCookie);
  }
  response.sendRedirect(REDIRECT_URL);
}

The HTTPClient Class is the class that creates a request to your J2EE in the background and could look something like this:

public class HTTPClient {

	public static org.apache.commons.httpclient.Cookie[] request(String REMOTE_USER) {

		Cookie[] cookies = null;
		
                // Define the url to your app on J2EE that is configured to use HTTP Header Authentication
		String url = "http://<yourenginehost>:<yourengineport>/<yourengineapp>";

		// Get initial state object
                HttpState initialState = new HttpState();
        
		// Create an instance of HttpClient.
		HttpClient client = new HttpClient();
		client.getHttpConnectionManager().getParams().setConnectionTimeout(30000);
		client.setState(initialState);
		
		// Create a method instance.
		GetMethod method = new GetMethod(url);
		    
		// Provide custom retry handler is necessary
		method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(3, false));

		try {
			
			// We add the Header for the Header Variable Login Module
			method.addRequestHeader("REMOTE_USER", REMOTE_USER);
			// Execute the method.
		    int statusCode = client.executeMethod(method);

		    if (statusCode != HttpStatus.SC_OK) {
		        System.err.println("Method failed: " + method.getStatusLine());
		    }
	    
		    // Get all the cookies from the J2EE response
		    cookies = client.getState().getCookies();
	    
		} catch (HttpException e) {
		      System.err.println("Fatal protocol violation: " + e.getMessage());
		      e.printStackTrace();
		} catch (IOException e) {
		      System.err.println("Fatal transport error: " + e.getMessage());
		      e.printStackTrace();
		} finally {
		      // Release the connection.
		      method.releaseConnection();
		}
		
		return cookies;
	}

Hope this helps

Cheers

Former Member
0 Likes

Hello Marcel,

in fact I did succeed, the problem was that even after domain-relaxation done by the J2EE, I had to change the domain of th SAP cookie to the bbbb.domain.com to be understood (I would have thought that all hosts in/under domain .domain would have accepted such a cookie but it seems that no...).

Thanks for the last post with code but I have a few more questions.

My current scenario is: in a first request get a SAP Logon Ticket from the Java Stack, then change its domain and then directly call the backend with it.

So everything's is in a Java Client application without using any redirection.

If I understand you, you're solution is from the Browser call a servlet (which is deployed on the Java Stack and has no authentication schema) by passing to it our http header. That servlet will transfer the http header (with the HttpClient app) in order to get from the Java Stack a SAP Logon ticket, and then to redirect to the resource and by sending back the cookie in client browser. Am I correct?

This way of doing would simplify the calls for sso for each new application needing authentication, instead of having all code each time in it...

So my problem now, is how to call the servlet from the client browser:

I'm trying to call my servlet from the browser but I don't succeed. I am able to understand how to reach a jsp from the Java Stack, but not to reach a servlet. I don't find the path to my servlet:


<FORM method="POST" action="SSORedirect2" >

I see that my servlet is deployed, but I don't how what path to give to my form to invoke the servlet, here follows my web.xml


  <?xml version="1.0" encoding="UTF-8" ?> 
  <!DOCTYPE web-app (View Source for full doctype...)> 
- <web-app>
  <display-name>WEB APP</display-name> 
  <description>WEB APP description</description> 
- <servlet>
  <servlet-name>SSOredirect2</servlet-name> 
  <servlet-class>com.atosorigin.examples.AuthenticatorServlet</servlet-class> 
  </servlet>
- <servlet>
  <servlet-name>SSORedirect2.jsp</servlet-name> 
  <jsp-file>/SSORedirect2.jsp</jsp-file> 
  </servlet>
- <security-constraint>
  <display-name>SecurityConstraint</display-name> 
- <web-resource-collection>
  <web-resource-name>WebResource</web-resource-name> 
  <url-pattern>/*</url-pattern> 
  <http-method>GET</http-method> 
  <http-method>POST</http-method> 
  </web-resource-collection>
- <auth-constraint>
  <role-name>DefaultSecurityRole</role-name> 
  </auth-constraint>
  </security-constraint>
- <security-role>
  <role-name>DefaultSecurityRole</role-name> 
  </security-role>
  </web-app>

I have also to pass my http header and the redirectUrl in the GET request.

Thx for your input very helpful,

Tanguy

Former Member
0 Likes

Hi Tanguy,

to tell you the truth I'm really unsure about what you are trying to achieve. When I started posting to your thread I thought all you wanted was trying to access your J2EE engine via Browser and authenticate against the engine using HTTP Header Variables. Nevermind:

Here are some answers to your question:

in fact I did succeed, the problem was that even after domain-relaxation done by the J2EE, I had to change the domain of th SAP cookie to the bbbb.domain.com to be understood (I would have thought that all hosts in/under domain .domain would have accepted such a cookie but it seems that no...).

The server does not care about the domain because Cookies in an HTTP Request do not contain any domain information. The domain is just important when the Cookie is set by the server so your Client (Browser) will know in which cases the Cookie may be sent or not. So if your domain is xxx.yyy.domain.com and your cookie is issued to .domain.com then your Browser will definitely sent it to all hosts under .domain.com (This includes xxx.yyy.domain.com etc.)

My current scenario is: in a first request get a SAP Logon Ticket from the Java Stack, then change its domain and then directly call the backend with it.

You can do that but there is no Client involved in this scenario. So this is useful if you just want to test the functionality (e.g. authentication to J2EE using Header Variables (This works finally!!!) and then use the fetched Logon Ticket to test SSO against any trusted Backend!!)

So everything's is in a Java Client application without using any redirection.

If I understand you, you're solution is from the Browser call a servlet (which is deployed on the Java Stack and has no authentication schema) by passing to it our http header.

No, you should initially authenticate somewhere! I thought that maybe you had some resource you access before accessing the Java Stack. This could be any application (e.g. deployed on a Tomcat or JBOSS or other server or if you like even SAP J2EE). After authenticating there you are aware of the username and could use it to procceed (e.g. Authenticate against the J2EE using the same user and HTTP Header authentication for that particular user!)

That servlet will transfer the http header (with the HttpClient app) in order to get from the Java Stack a SAP Logon ticket, and then to redirect to the resource and by sending back the cookie in client browser. Am I correct?

This was just a suggestion because I realized that there was no Client ever involved in any of your testing (looked strange to me!). I was just thinking that it would be easier for you to just get the Cookie into your Browser so your Browser would do the rest for you (in your case finally send the Logon Ticket Cookie to your Backend to test SSO using Logon Tickets!).

The AuthenticatorServlet somehow serves as a Proxy to your client because your client is not able to set the Header Variable. That's why I initially suggested to use a Proxy (e.g. Apache) for that purpose. The problem is just that if you use a Proxy you will have to tell it somehow which username it should set in the Header Variable (e.g. using a URL Parameter or using a personalized client certificate and fetch the username (e.g. cn=<username> from the certificate!)

This way of doing would simplify the calls for sso for each new application needing authentication, instead of having all code each time in it...

I'm stuck again! Do you want to authenticate an End User or do you want to authenticate an application that needs to call any resources in your Backend that requires authentication?

So my problem now, is how to call the servlet from the client browser:

I'm trying to call my servlet from the browser but I don't succeed. I am able to understand how to reach a jsp from the Java Stack, but not to reach a servlet. I don't find the path to my servlet:

<FORM method="POST" action="SSORedirect2" >

A JSP is a servlet too. There is just no JAVA Class involved!

You do not need any POST Request to invoke a Servlet.

I see that my servlet is deployed, but I don't how what path to give to my form to invoke the servlet, here follows my web.xml

  <?xml version="1.0" encoding="UTF-8" ?> 
  <!DOCTYPE web-app (View Source for full doctype...)> 
- <web-app>
  <display-name>WEB APP</display-name> 
  <description>WEB APP description</description> 
- <servlet>
  <servlet-name>SSOredirect2</servlet-name> 
  <servlet-class>com.atosorigin.examples.AuthenticatorServlet</servlet-class> 
  </servlet>
- <servlet>
  <servlet-name>SSORedirect2.jsp</servlet-name> 
  <jsp-file>/SSORedirect2.jsp</jsp-file> 
  </servlet>
- <security-constraint>
  <display-name>SecurityConstraint</display-name> 
- <web-resource-collection>
  <web-resource-name>WebResource</web-resource-name> 
  <url-pattern>/*</url-pattern> 
  <http-method>GET</http-method> 
  <http-method>POST</http-method> 
  </web-resource-collection>
- <auth-constraint>
  <role-name>DefaultSecurityRole</role-name> 
  </auth-constraint>
  </security-constraint>
- <security-role>
  <role-name>DefaultSecurityRole</role-name> 
  </security-role>
  </web-app>

If you have an AuthenticatorServlet Class all you need is to add the Servlet Mapping in your web.xml file

e.g.

<servlet>
  <description>
  </description>
  <display-name>AuthenticatorServlet</display-name>
  <servlet-name>AuthenticatorServlet</servlet-name>
  <servlet-class>com.atosorigin.examples.AuthenticatorServlet</servlet-class>
</servlet>
<servlet-mapping>
  <servlet-name>AuthenticatorServlet</servlet-name>
  <url-pattern>/AuthenticatorServlet</url-pattern>
</servlet-mapping>

You can directly call the Servlet in your Browser by calling the URL provided in the url-pattern of your Servlet mapping ( in this case /AuthenticatorServlet). The engine will invoke the Class "com.atosorigin.examples.AuthenticatorServlet" in the background and do whatever you defined there!

I have also to pass my http header and the redirectUrl in the GET request.

If you like! I just suggested this for testing purposes. As I stated before you need a way to tell your proxy (or in your case AuthenticatorServlet) which user should be set when calling the Engine in order to authenticate using HTTP Header. You could use the URL Paramater to define the user you actually want to use when you set the Header Variable.

I just introduced the redirectURL because you were talking about redirects all the time. So if you finally want to call the Backend you could define the Backend URL in the redirectURL Parameter and the Servlet will make sure that you are redirected to this location after the whole process!

Thx for your input very helpful,

But again 0 points

Cheers

Former Member
0 Likes

Hey,

did you finally solve all issues?

Cheers

Former Member
0 Likes

Hi Marcel,

no it's still not working. We are struggling with the SAP Logon Ticket, when I use your code as it is, I only get the Set-Cookie for JSESSIONID and for saplb_*. I see them with the fiddler tool but when I change the java code and output with strings, I see that I get the SAP Logon Ticket and that I have to add it by hand to my new http request. The redirect method seems not to pass the cookie along. With my client java application, I succeed to see the BSP/Web dynpro html code in the http body but when trying through a servlet the cookie seem not to be redirected automatically.

If you have an idea... I have activated http logs on my backend but the level is not detailed enough to see the headers parameters.

Best regards,

Tanguy