This tutorial will show Java developers how to leverage Windows Azure Active Directory to enable single sign-on for users of Office 365 customers. You will learn how to:
This tutorial uses a specific application server, but if you are an experienced Java developer, the process described below can be applied to other environments as well. The following development environment prerequisites are required for this tutorial:
This step describes how to create a simple Java application that will represent a protected resource. Access to this resource will be granted through federated authentication managed by the company's STS, which is described later in the tutorial.
This step describes how an administrator of a Windows Azure Active Directory customer provisions the Java application in their tenant and configures single sign-on. After this step is accomplished, the company's employees can authenticate to the web application using their Office 365 accounts.
The provisioning process begins by creating a new Service Principal for the application. Service Principals are used by Windows Azure Active Directory to register and authenticate applications to the directory.
The web application has now been provisioned in the directory and it can be used for web single sign-on by company employees.
This step shows you how to add support for federated login using Windows Identity Foundation (WIF) and the waad-federation library you downloaded as part of the sample code package in the prerequisites. You will also add a login page and configure trust between the application and the directory tenant.
-
In JBoss Developer Studio, click File, then click Import.
-
On the Import dialog, expand the Maven folder, click Existing Maven Projects, then click Next.
-
On the Import Maven Projects dialog, set the Root Directory path to the location where you downloaded the waad-federation library in the sample code. Then, select the checkbox next to the pom.xml file from the waad-federation project and click Finish.
-
Expand the sample project, right-click the pom.xml file, click Open With, then click Text Editor.
-
In the pom.xml file, add the following XML inside the <project> section, then save the file:
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>3.0-alpha-1</version>
</dependency>
<dependency>
<groupId>com.microsoft.samples.waad.federation</groupId>
<artifactId>waad-federation</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies> -
Right-click the sample project, click Maven, then click Update Project. In the Update Maven Project dialog, click OK. This step will update your project with the pom.xml changes.
-
Right-click the sample project, click New, then click Filter.
-
In the Create Filter dialog, type FederationFilter for the Class name entry, then click Finish.
-
The automatically generated FederationFilter.java file will open. Replace its code with the following code and save the file:
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.regex.*;
import com.microsoft.samples.federation.FederatedLoginManager; import com.microsoft.samples.federation.URLUTF8Encoder;
public class FederationFilter implements Filter {
private String loginPage;
private String allowedRegex;
public void init(FilterConfig config) throws ServletException {
this.loginPage = config.getInitParameter("login-page-url");
this.allowedRegex = config.getInitParameter("allowed-regex");
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (request instanceof HttpServletRequest) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
if (!httpRequest.getRequestURL().toString().contains(this.loginPage)) {
FederatedLoginManager loginManager = FederatedLoginManager.fromRequest(httpRequest);
boolean allowedUrl = Pattern.compile(this.allowedRegex).matcher(httpRequest.getRequestURL().toString()).find();
if (!allowedUrl && !loginManager.isAuthenticated()) {
HttpServletResponse httpResponse = (HttpServletResponse) response;
String encodedReturnUrl = URLUTF8Encoder.encode(httpRequest.getRequestURL().toString());
httpResponse.setHeader("Location", this.loginPage + "?returnUrl=" + encodedReturnUrl);
httpResponse.setStatus(302);
return;
}
}
}
chain.doFilter(request, response);
}
public void destroy() {
}
} -
In Project Explorer, expand the src, then main, then webapp folders. Right-click the web.xml file, click Open With, then click Text Editor.
-
In the web.xml file, you will add a filter to handle secured and unsecured pages, and it will also redirect unauthenticated users to the login page (specified as the login-page-url filter parameter). However, if a URL matches the regex specified in the allowed-regex filter parameter, it will not be filtered. Add the following XML within the <web-app> section, then save the web.xml file.
<filter>
<filter-name>FederationFilter</filter-name>
<filter-class>FederationFilter</filter-class>
<init-param>
<param-name>login-page-url</param-name>
<param-value>/sample/login.jsp</param-value>
</init-param>
<init-param>
<param-name>allowed-regex</param-name>
<param-value>(\/sample\/login.jsp|\/sample\/wsfed-saml|\/sample\/oauth)</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>FederationFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping> -
To create a login page, right-click the sample project, click New, then click JSP File.
-
On the New JSP File dialog, change the path for the new file to sample/src/main/webapp. Then, name the file login.jsp and click Finish.
-
The new login.jsp file will open automatically. Replace the automatically generated code with the following, then save the file:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ page import="com.microsoft.samples.federation.*"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Login Page</title>
</head>
<body>
<h3>Login Page</h3>
<a href="<%=FederatedLoginManager.getFederatedLoginUrl(request.getParameter("returnUrl"))%>"><%=FederatedConfiguration.getInstance().getStsFriendlyName()%></a>
</body>
</html> -
In Project Explorer, expand the /src/main folder of the sample project. Right-click the resources folder, click New, then click Other.
-
From the New dialog, expand the JBoss Tools Web folder, click Properties File, then click Next.
-
On the New File Properties dialog, name the file federation, then click Finish.
-
In Project Explorer, expand the src/main/resources folder of the sample project. Right-click the federation.properties file, click Open With, then click Text Editor.
-
In the federation.properties file, include the following configuration entries, then save the file:
federation.trustedissuers.issuer=https://accounts.accesscontrol.windows.net/v2/wsfederation
federation.trustedissuers.thumbprint=qY+Drf20Zz+A4t2we3PebCopoCugO76My+JMVsqNBFc=
federation.trustedissuers.friendlyname=Fabrikam
federation.audienceuris=spn:7829c758-2bef-43df-a685-717089474505
federation.realm=spn:7829c758-2bef-43df-a685-717089474505
federation.reply=https://localhost:8443/sample/wsfed-saml
Note Theaudienceurisandrealmvalues must be prefaced by "spn:".
-
Now you need to create a new Servlet. Right-click the sample project, click New, click Other, then click Servlet.
-
On the Create Servlet dialog, provide a Class name of FederationServlet and click Finish.
-
The FederationServlet.java file is automatically opened. Replace its contents with the following code, then safe the file:
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.microsoft.samples.federation.FederatedAuthenticationListener;
import com.microsoft.samples.federation.FederatedLoginManager;
import com.microsoft.samples.federation.FederatedPrincipal;
import com.microsoft.samples.federation.FederationException;
public class FederationServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String token = request.getParameter("wresult").toString();
if (token == null) {
response.sendError(400, "You were supposed to send a wresult parameter with a token");
}
FederatedLoginManager loginManager = FederatedLoginManager.fromRequest(request, new SampleAuthenticationListener());
try {
loginManager.authenticate(token, response);
} catch (FederationException e) {
response.sendError(500, "Oops! and error occurred.");
}
}
private class SampleAuthenticationListener implements FederatedAuthenticationListener {
@Override
public void OnAuthenticationSucceed(FederatedPrincipal principal) {
// ***
// do whatever you want with the principal object that contains the token's claims
// ***
}
}
} -
In Project Explorer, expand the src/main/webapp/WEB-INF folder. Right-click the web.xml file, click Open With, then click Text Editor.
-
In the web.xml file, replace the /FederationServlet setting in the <url-pattern> section with /ws-saml. For example:
<servlet>
<description></description>
<display-name>FederationServlet</display-name>
<servlet-name>FederationServlet</servlet-name>
<servlet-class>FederationServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>FederationServlet</servlet-name>
<url-pattern>/wsfed-saml</url-pattern>
</servlet-mapping> -
In Project Explorer, expand the src/main/webapp folder. Right-click the index.jsp file, click Open With, then click Text Editor.
-
In the index.jsp file, replace the existing code with the following code, then save the file:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ page import="com.microsoft.samples.federation.*"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Index Page</title>
</head>
<body>
<h3>Welcome <strong><%=FederatedLoginManager.fromRequest(request).getPrincipal().getName()%></strong>!</h3>
<h2>Claim list:</h2>
<ul>
<% for (Claim claim : FederatedLoginManager.fromRequest(request).getClaims()) { %>
<li><%= claim.toString()%></li>
<% } %>
</ul>
</body>
</html> -
In Project Explorer, expand the src/main/webapp/WEB-INF folder. Right-click the web.xml file, click Open With, then click Text Editor.
-
We will now enable SSL for the application. In the web.xml file, insert the following <security-constraint> section within the <web-app> section, then save the file:
<security-constraint>
<web-resource-collection>
<web-resource-name>SSL Forwarding</web-resource-name>
<url-pattern>/*</url-pattern>
<http-method>POST</http-method>
<http-method>GET</http-method>
</web-resource-collection>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint> Note Before proceeding, ensure that JBoss Server is already configured to support SSL.
-
Now we are ready to run the application end-to-end. Right-click the sample project, click Run As, then click Run On Server. Accept the values that you specified before, and click Finish.
-
The JBoss browser will open the login page. If you click on the Awesome Computers link, you will be redirected to the Office 365 Identity Provider page, where you can log in using your directory tenant credentials. For example, john.doe@fabrikam.onmicrosoft.com.
-
After you have logged in, you will be redirected again to the secured page (sample/index.jsp) as an authenticated user.
This tutorial has shown you how to create and configure a single tenant Java application that uses the single sign-on capabilities of Windows Azure Active Directory.