Рубрики
Без рубрики

Единый вход Azure AD в веб-приложении Java, конфигурации единого входа ADFS

Единый вход Azure AD в веб-приложении java, Пример единого входа Azure Active Directory, руководство по настройке единого входа ADFS, код проекта единого входа Azure AD.

Автор оригинала: Pankaj Kumar.

Единый администратор Azure AD

Функция единого входа становится популярной среди разработчиков для управления доступом к приложениям при разработке многоликих приложений благодаря своим замечательным преимуществам. Azure Active Directory-одно из самых популярных решений, используемых для управления каталогами, управления доступом к приложениям и управления защищенной идентификацией, которое также предлагает эффективную функцию единого входа.

Недавно я интегрировал единый вход Azure AD с веб-приложением Java, а также синхронизировал его с существующей системой управления идентификацией. Я использовал ADFS 2016 Служб федерации Active Directory. В этом блоге я делюсь процессом интеграции в трех разделах.

  1. Как зарегистрировать приложение Java в Azure AD
  2. Как реализовать библиотеку ADAL в Java-приложении
  3. Конфигурация ADFS для единого входа в систему единого входа

Как зарегистрировать приложение Java в Azure AD

Следуйте инструкциям, приведенным по ссылке ниже, чтобы зарегистрировать приложение Java в Azure AD с помощью библиотеки аутентификации Active Directory для Java (ADAL4J) и получить токен доступа JWT.

Следуйте инструкциям, приведенным по ссылке ниже, чтобы зарегистрировать приложение Java в Azure AD с помощью библиотеки аутентификации Active Directory для Java (ADAL4J) и получить токен доступа JWT.

Как реализовать библиотеку ADAL в веб-приложении Java

Процесс внедрения библиотеки ADAL в Java-приложение состоит всего из шести этапов:

  1. Добавление зависимостей библиотеки ADAL
  2. Зарегистрируйте фильтр ADSL и добавьте параметры контекста в свой web.xml (Информирование приложения Java о конфигурации приложения Azure)
  3. Создать Фильтр Adsl
  4. Создайте безопасный контроллер
  5. Создайте класс AuthHelper
  6. Создать a.jsp

Добавьте зависимости библиотеки adal в приложение java


	com.microsoft.azure
	adal4j
	1.1.1


	com.nimbusds
	oauth2-oidc-sdk
	4.5

Зарегистрируйте фильтр ADSL и добавьте параметры контекста в свой web.xml


	authority
	https://login.windows.net/


	tenant
	YOUR_TENANT_NAME



	AdalFilter
	com.azilen.aad.AdalFilter
	
		client_id
		
	
	
		secret_key
		
	


	 AdalFilter 
	/secure/*

Замените значения ниже фактическими, которые вы получили с портала Azure при регистрации приложения, как описано в предыдущем шаге.

ВАШ идентификатор КЛИЕНТА и ВАШ СЕКРЕТНЫЙ КЛЮЧ, полномочия (это страница входа в azure ad), имя пользователя-это название организации.

Создать Адольфа Гитлера

Фильтр Adsl проверяет, сохранено ли в текущем сеансе действительное ИМЯ PRINCIPAL_SESSION_NAME , если нет, он перенаправит на страницу входа в Azure (Полномочия).

public class AdalFilter implements Filter {

	private String clientId = "";
	private String clientSecret = "";
	private String tenant = "";
	private String authority;

	public void destroy() {

	}

	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

		if (request instanceof HttpServletRequest) {
			HttpServletRequest httpRequest = (HttpServletRequest) request;
			HttpServletResponse httpResponse = (HttpServletResponse) response;
			try {

				String currentUri = request.getScheme() + "://" + request.getServerName()
						+ ("http".equals(request.getScheme()) && request.getServerPort() == 80
								|| "https".equals(request.getScheme()) && request.getServerPort() == 443 ? "" : ":" + request.getServerPort())
						+ httpRequest.getRequestURI();
				String fullUrl = currentUri + (httpRequest.getQueryString() != null ? "?" + httpRequest.getQueryString() : "");
				// check if user has a session
				if (!AuthHelper.isAuthenticated(httpRequest)) {
					if (AuthHelper.containsAuthenticationData(httpRequest)) {
						Map params = new HashMap();
						for (String key : request.getParameterMap().keySet()) {
							params.put(key, request.getParameterMap().get(key)[0]);
						}
						AuthenticationResponse authResponse = AuthenticationResponseParser.parse(new URI(fullUrl), params);
						if (AuthHelper.isAuthenticationSuccessful(authResponse)) {

							AuthenticationSuccessResponse oidcResponse = (AuthenticationSuccessResponse) authResponse;
							AuthenticationResult result = getAccessToken(oidcResponse.getAuthorizationCode(), currentUri);
							createSessionPrincipal(httpRequest, result);
						} else {
							AuthenticationErrorResponse oidcResponse = (AuthenticationErrorResponse) authResponse;
							throw new Exception(String.format("Request for auth code failed: %s - %s", oidcResponse.getErrorObject().getCode(),
									oidcResponse.getErrorObject().getDescription()));
						}
					} else {
						// not authenticated
						httpResponse.setStatus(302);
						httpResponse.sendRedirect(getRedirectUrl(currentUri));
						return;
					}
				} else {
					// if authenticated, how to check for valid session?
					AuthenticationResult result = AuthHelper.getAuthSessionObject(httpRequest);

					if (httpRequest.getParameter("refresh") != null) {
						result = getAccessTokenFromRefreshToken(result.getRefreshToken(), currentUri);
					} else {
						if (httpRequest.getParameter("cc") != null) {
							result = getAccessTokenFromClientCredentials();
						} else {
							if (result.getExpiresOnDate().before(new Date())) {
								result = getAccessTokenFromRefreshToken(result.getRefreshToken(), currentUri);
							}
						}
					}
					createSessionPrincipal(httpRequest, result);
				}
			} catch (Throwable exc) {
				httpResponse.setStatus(500);
				request.setAttribute("error", exc.getMessage());
				httpResponse.sendRedirect(((HttpServletRequest) request).getContextPath() + "/error.jsp");
			}
		}
		chain.doFilter(request, response);
	}

	private AuthenticationResult getAccessTokenFromClientCredentials() throws Throwable {
		AuthenticationContext context = null;
		AuthenticationResult result = null;
		ExecutorService service = null;
		try {
			service = Executors.newFixedThreadPool(1);
			context = new AuthenticationContext(authority + tenant + "/", true, service);
			Future future = context.acquireToken("https://graph.windows.net", new ClientCredential(clientId, clientSecret),
					null);
			result = future.get();
		} catch (ExecutionException e) {
			throw e.getCause();
		} finally {
			service.shutdown();
		}	

		if (result == null) {
			throw new ServiceUnavailableException("authentication result was null");
		}
		return result;
	}

	private AuthenticationResult getAccessTokenFromRefreshToken(String refreshToken, String currentUri) throws Throwable {
		AuthenticationContext context = null;
		AuthenticationResult result = null;
		ExecutorService service = null;
		try {
			service = Executors.newFixedThreadPool(1);
			context = new AuthenticationContext(authority + tenant + "/", true, service);
			Future future = context.acquireTokenByRefreshToken(refreshToken, new ClientCredential(clientId, clientSecret), null,
					null);
			result = future.get();
		} catch (ExecutionException e) {
			throw e.getCause();
		} finally {
			service.shutdown();
		}

		if (result == null) {
			throw new ServiceUnavailableException("authentication result was null");
		}
		return result;

	}

	private AuthenticationResult getAccessToken(AuthorizationCode authorizationCode, String currentUri) throws Throwable {
		String authCode = authorizationCode.getValue();
		ClientCredential credential = new ClientCredential(clientId, clientSecret);
		AuthenticationContext context = null;
		AuthenticationResult result = null;
		ExecutorService service = null;
		try {
			service = Executors.newFixedThreadPool(1);
			context = new AuthenticationContext(authority + tenant + "/", true, service);
			Future future = context.acquireTokenByAuthorizationCode(authCode, new URI(currentUri), credential, null);
			result = future.get();
		} catch (ExecutionException e) {
			throw e.getCause();
		} finally {
			service.shutdown();
		}

		if (result == null) {
			throw new ServiceUnavailableException("authentication result was null");
		}
		return result;
	}

	private void createSessionPrincipal(HttpServletRequest httpRequest, AuthenticationResult result) throws Exception {
		httpRequest.getSession().setAttribute(AuthHelper.PRINCIPAL_SESSION_NAME, result);
	}

	private String getRedirectUrl(String currentUri) throws UnsupportedEncodingException {
		String redirectUrl = authority + this.tenant
				+ "/oauth2/authorize?response_type=code%20id_token&scope=openid&response_mode=form_post&redirect_uri="
				+ URLEncoder.encode(currentUri, "UTF-8") + "&client_id=" + clientId + "&resource=https%3a%2f%2fgraph.windows.net" + "&nonce="
				+ UUID.randomUUID() + "&site_id=500879";
		return redirectUrl;
	}

	public void init(FilterConfig config) throws ServletException {
		clientId = config.getInitParameter("client_id");
		authority = config.getServletContext().getInitParameter("authority");
		tenant = config.getServletContext().getInitParameter("tenant");
		clientSecret = config.getInitParameter("secret_key");
	}

}

Создайте безопасный контроллер

@Controller
@RequestMapping("/secure/aad")
public class AadController { 
@RequestMapping(method = { RequestMethod.GET, RequestMethod.POST })
public String getDirectoryObjects(ModelMap model, HttpServletRequest httpRequest) {
	HttpSession session = httpRequest.getSession();
	log.info("session: " + session);
	AuthenticationResult result = (AuthenticationResult) session.getAttribute(AuthHelper.PRINCIPAL_SESSION_NAME);
	if (result == null) {
		model.addAttribute("error", new Exception("AuthenticationResult not found in session."));
		return "/error";
	} else {
		try {
			log.info("JWT token details:-");
			JWT jwt = JWTParser.parse(result.getIdToken());
			for (String key : jwt.getJWTClaimsSet().getAllClaims().keySet()) {
				log.info(key + ":" + jwt.getJWTClaimsSet().getAllClaims().get(key));
			}
			model.addAttribute("user", jwt.getJWTClaimsSet().getStringClaim("unique_name"));
		} catch (ParseException e) {
			log.error("Exception:", e);
		}
	}
	return "/secure/aad";
}
}

Создайте класс AuthHelper

public final class AuthHelper {

	public static final String PRINCIPAL_SESSION_NAME = "principal";

	private AuthHelper() {
	}

	public static boolean isAuthenticated(HttpServletRequest request) {
		return request.getSession().getAttribute(PRINCIPAL_SESSION_NAME) != null;
	}

	public static AuthenticationResult getAuthSessionObject(HttpServletRequest request) {
		return (AuthenticationResult) request.getSession().getAttribute(PRINCIPAL_SESSION_NAME);
	}

	public static boolean containsAuthenticationData(HttpServletRequest httpRequest) {
		Map map = httpRequest.getParameterMap();
		return (httpRequest.getMethod().equalsIgnoreCase("POST") 
					|| httpRequest.getMethod().equalsIgnoreCase("GET")) 
				&& (httpRequest.getParameterMap().containsKey(AuthParameterNames.ERROR)
						|| httpRequest.getParameterMap().containsKey(AuthParameterNames.ID_TOKEN)
						|| httpRequest.getParameterMap().containsKey(AuthParameterNames.CODE));
	}

	public static boolean isAuthenticationSuccessful(AuthenticationResponse authResponse) {
		return authResponse instanceof AuthenticationSuccessResponse;
	}
}

Создайте файл.jsp






AAD Secure Page



	

Welcome, ${user}

Тест веб-приложения Java для единого входа Azure AD

На изображениях ниже показана страница входа в Azure AD и страница успеха после успешной аутентификации.

Конфигурация ADFS для единого входа (единого входа)

Чтобы настроить ADFS 2016, сначала убедитесь, что приложение работает по протоколу SSL. Процесс настройки ADFS для включения синхронизации между Azure AD и существующей системой управления удостоверениями состоит из трех этапов:

  1. Добавьте Доверие Проверяющей Стороны
  2. Добавить идентификатор клиента
  3. Выполните команду авторизации в PowerShell сервера ADFS

Добавьте Доверие Проверяющей Стороны

  • На странице приветствия выберите С учетом претензий
  • При выборе источника данных выберите Введите данные о проверяющей стороне вручную
  • Укажите любое отображаемое имя
  • Настройка сертификата добавление самозаверяющего сертификата
  • Настройка URL-адреса: выберите любой из них в соответствии с вашим протоколом ADFS
    • Установите флажок Включить поддержку пассивного протокола WS-Федерации . В разделе URL-адрес пассивного протокола WS-Федерации проверяющей стороны введите URL-адрес для этого доверия проверяющей стороны.
    • Установите флажок Включить поддержку протокола SAML 2.0 WebSSO . В разделе URL-адрес службы единого входа проверяющей стороны 2.0 введите URL-адрес конечной точки службы языка разметки утверждений безопасности (SAML) для этого доверия проверяющей стороны.
  • Настройка идентификаторов: Добавьте URL-адрес приложения в Идентификатор доверия Проверяющей стороны.
  • Выберите Политику контроля доступа: Выберите Разрешить все (Выберите в соответствии с вашей средой)
  • Готовы добавить доверие: Просмотрите этот параметр
  • Заканчивать.

Ссылка: https://docs.microsoft.com/en-us/windows-server/identity/ad-fs/operations/create-a-relying-party-trust

Добавьте идентификатор клиента с помощью следующей команды

Add-ADFSClient -Name "SampleApplication" -ClientId "" -RedirectUri @("REDIRECTURI") -Description "OAuth 2.0 client for our Test application"

Ссылка: https://docs.microsoft.com/en-us/powershell/module/adfs/add-adfsclient?view=win10-ps

Выполните следующую команду авторизации в PowerShell сервера ADFS

Grant-ADFSApplicationPermission -ClientRoleIdentifier "" -ServerRoleIdentifier "" -ScopeNames "allatclaims","openid"
AUTHORITY eg: https://login.windows.net/

Вывод

Независимо от размера и характера любого приложения разработчики могут обеспечить централизованное управление доступом на основе политик к приложению, используя стандартную платформу Azure Active Directory. Эта интеграция и синхронизация улучшили мои знания об удовлетворении бизнес-потребностей, одновременно упростив путешествие пользователя по системе.

Об авторе Виджай-энергичный программист, который последние пять лет дышит технологиями. Он является старшим инженером-программистом в Azilen Technologies , использующим свой замечательный опыт в Spring , Hibernate , Liferay и JSF.