This repository has been archived by the owner on Aug 28, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 456
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
3d92eb6
commit d3a887f
Showing
37 changed files
with
2,348 additions
and
166 deletions.
There are no files selected for viewing
42 changes: 42 additions & 0 deletions
42
activedirectory/azure-ad-integration-spring-boot-autoconfigure-sample/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
## Overview | ||
This sample illustrates how to use `azure-ad-integration-spring-boot-autoconfigure` pre-release package to plugin JWT token filter into Spring Security filter chain. The filter injects `UserPrincipal` object that is associated with the thread of the current user request. User's AAD membership info, along with token claimsset, JWS object etc. are accessible from the object which can be used for role based authorization. Methods like `isMemberOf` is also supported. | ||
|
||
### Get started | ||
The sample is composed of two layers: Angular JS client and Spring Boot RESTful Web Service. You need to make some changes to get it working with your Azure AD tenant on both sides. | ||
|
||
#### Application.properties | ||
You need to have an registered app in your Azure AD tenant and create a security key. | ||
Put Application ID and Key in `clientId` and `clientSecret` respectively e.g. | ||
`azure.activedirectory.clientId=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` | ||
`azure.activedirectory.clientSecret=ABCDEFGHIJKLMNOOPQRSTUVWXYZABCDEFGHIJKLMNOPQ` | ||
List all the AAD groups `ActiveDirectoryGroups` that you want to have a Spring Security role object mapping to it. The role objects can then be used to manage access to resources that is behind Spring Security. e.g. | ||
`azure.activedirectory.ActiveDirectoryGroups=group1,group2` | ||
You can use `@PreAuthorize` annotation or `UserPrincipal` to manage access to web API based on user's group membership. You will need to change `ROLE_group1` to groups you want to allow to access the API or you will get "Access is denied". | ||
|
||
##### Note: The sample retrieves user's group membership using Azure AD graph API which requires the registered app to have `Access the directory as the signed-in user` under `Delegated Permissions`. You need AAD admin privilege to be able to grant the permission in API ACCESS -> Required permission. | ||
|
||
|
||
#### Angular JS | ||
In `app.js`, make following changes. The client leverages Azure AD library for JS to handle AAD authentication in single page application. The following snippet of code configures adal provider for your registered app. | ||
``` | ||
adalProvider.init( | ||
{ | ||
instance: 'https://login.microsoftonline.com/', | ||
tenant: 'your-aad-tenant', | ||
clientId: 'your-application-id', | ||
extraQueryParameter: 'nux=1', | ||
cacheLocation: 'localStorage', | ||
}, | ||
$httpProvider | ||
); | ||
``` | ||
|
||
### Give it a run | ||
* Go to `path-to-azure-spring-boot-starters`, run `mvn clean package`. | ||
* `cd activedirectory\azure-ad-integration-spring-boot-autoconfigure` | ||
* `mvn install` | ||
* `cd activedirectory\azure-ad-integration-spring-boot-autoconfigure-sample` | ||
* `mvn spring-boot:run` | ||
* If running locally, browse to `http://localhost:8080` and click `Login` or `Todo List`, your brower will be redirected to `https://login.microsoftonline.com/` for authentication. | ||
* Upon successful login, `Todo List` will give you a default item and you can perform add, update or delete operation. The backend RESTful API will accept or deny your request based on authenticated user roles. |
189 changes: 189 additions & 0 deletions
189
activedirectory/azure-ad-integration-spring-boot-autoconfigure-sample/pom.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" | ||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<modelVersion>4.0.0</modelVersion> | ||
|
||
<parent> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter-parent</artifactId> | ||
<version>1.5.4.RELEASE</version> | ||
<relativePath/> <!-- lookup parent from repository --> | ||
</parent> | ||
|
||
<groupId>com.microsoft.azure</groupId> | ||
<artifactId>azure-ad-integration-spring-boot-autoconfigure-sample</artifactId> | ||
<version>0.0.1-SNAPSHOT</version> | ||
<packaging>jar</packaging> | ||
|
||
<name>Azure AD Spring Security Integration Spring Boot Autoconfigure Sample</name> | ||
<description>Azure AD Spring Security Integration Spring Boot Autoconfigure Sample</description> | ||
<url>https://github.com/Microsoft/azure-spring-boot-starters</url> | ||
|
||
<licenses> | ||
<license> | ||
<name>MIT</name> | ||
<url>https://github.com/Microsoft/azure-spring-boot-starters/blob/master/LICENSE</url> | ||
<distribution>repo</distribution> | ||
</license> | ||
</licenses> | ||
|
||
<developers> | ||
<developer> | ||
<id>yaweiw</id> | ||
<name>Yawei Wang</name> | ||
<email>[email protected]</email> | ||
</developer> | ||
</developers> | ||
|
||
<scm> | ||
<connection>scm:git:git://github.com/Microsoft/azure-spring-boot-starters.git</connection> | ||
<developerConnection>scm:git:ssh://github.com:Microsoft/azure-spring-boot-starters.git</developerConnection> | ||
<url>https://github.com/Microsoft/azure-spring-boot-starters/tree/master</url> | ||
</scm> | ||
|
||
<dependencyManagement> | ||
<dependencies> | ||
<dependency> | ||
<groupId>com.microsoft.azure</groupId> | ||
<artifactId>azure-spring-boot-starter-bom</artifactId> | ||
<version>0.1.5</version> | ||
<type>pom</type> | ||
<scope>import</scope> | ||
</dependency> | ||
</dependencies> | ||
</dependencyManagement> | ||
|
||
<dependencies> | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter-web</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.springframework.security</groupId> | ||
<artifactId>spring-security-config</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.springframework.security.oauth</groupId> | ||
<artifactId>spring-security-oauth2</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>com.microsoft.azure</groupId> | ||
<artifactId>azure-ad-integration-spring-boot-autoconfigure</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>com.fasterxml.jackson.core</groupId> | ||
<artifactId>jackson-core</artifactId> | ||
</dependency> | ||
</dependencies> | ||
|
||
<properties> | ||
<java.version>1.8</java.version> | ||
<project.rootdir>${project.basedir}/../..</project.rootdir> | ||
<maven-compiler-plugin.version>3.6.1</maven-compiler-plugin.version> | ||
<maven-checkstyle-plugin.version>2.17</maven-checkstyle-plugin.version> | ||
<findbugs-maven-plugin.version>3.0.0</findbugs-maven-plugin.version> | ||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> | ||
</properties> | ||
|
||
<build> | ||
<pluginManagement> | ||
<plugins> | ||
<plugin> | ||
<groupId>org.apache.maven.plugins</groupId> | ||
<artifactId>maven-compiler-plugin</artifactId> | ||
<version>${maven-compiler-plugin.version}</version> | ||
</plugin> | ||
<plugin> | ||
<groupId>org.apache.maven.plugins</groupId> | ||
<artifactId>maven-checkstyle-plugin</artifactId> | ||
<version>${maven-checkstyle-plugin.version}</version> | ||
</plugin> | ||
<plugin> | ||
<groupId>org.codehaus.mojo</groupId> | ||
<artifactId>findbugs-maven-plugin</artifactId> | ||
<version>${findbugs-maven-plugin.version}</version> | ||
</plugin> | ||
</plugins> | ||
</pluginManagement> | ||
<plugins> | ||
<plugin> | ||
<groupId>org.apache.maven.plugins</groupId> | ||
<artifactId>maven-compiler-plugin</artifactId> | ||
<configuration> | ||
<source>${java.version}</source> | ||
<target>${java.version}</target> | ||
</configuration> | ||
</plugin> | ||
<plugin> | ||
<groupId>org.apache.maven.plugins</groupId> | ||
<artifactId>maven-checkstyle-plugin</artifactId> | ||
<executions> | ||
<execution> | ||
<id>validate</id> | ||
<phase>validate</phase> | ||
<configuration> | ||
<configLocation>${project.rootdir}/common/config/checkstyle.xml</configLocation> | ||
<encoding>UTF-8</encoding> | ||
<consoleOutput>true</consoleOutput> | ||
<failsOnError>true</failsOnError> | ||
<failOnViolation>true</failOnViolation> | ||
<includeTestSourceDirectory>true</includeTestSourceDirectory> | ||
</configuration> | ||
<goals> | ||
<goal>check</goal> | ||
</goals> | ||
</execution> | ||
</executions> | ||
<configuration> | ||
<linkXRef>false</linkXRef> | ||
</configuration> | ||
<inherited>true</inherited> | ||
</plugin> | ||
<plugin> | ||
<groupId>org.codehaus.mojo</groupId> | ||
<artifactId>findbugs-maven-plugin</artifactId> | ||
<configuration> | ||
<effort>Max</effort> | ||
<threshold>Low</threshold> | ||
<xmlOutput>true</xmlOutput> | ||
<findbugsXmlOutputDirectory>${project.build.directory}/findbugs | ||
</findbugsXmlOutputDirectory> | ||
<excludeFilterFile>${project.rootdir}/common/config/findbugs-exclude.xml</excludeFilterFile> | ||
</configuration> | ||
<dependencies> | ||
<dependency> | ||
<groupId>org.apache.ant</groupId> | ||
<artifactId>ant</artifactId> | ||
<version>1.9.4</version> | ||
</dependency> | ||
</dependencies> | ||
<executions> | ||
<execution> | ||
<phase>compile</phase> | ||
<goals> | ||
<goal>check</goal> | ||
</goals> | ||
</execution> | ||
</executions> | ||
</plugin> | ||
<plugin> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-maven-plugin</artifactId> | ||
<executions> | ||
<execution> | ||
<goals> | ||
<goal>repackage</goal> | ||
</goals> | ||
</execution> | ||
</executions> | ||
</plugin> | ||
</plugins> | ||
</build> | ||
|
||
|
||
</project> |
17 changes: 17 additions & 0 deletions
17
...t/azure/autoconfigure/aad/AzureAdIntegrationSpringBootAutoconfigureSampleApplication.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
/** | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. See LICENSE in the project root for | ||
* license information. | ||
*/ | ||
package com.microsoft.azure.autoconfigure.aad; | ||
|
||
import org.springframework.boot.SpringApplication; | ||
import org.springframework.boot.autoconfigure.SpringBootApplication; | ||
|
||
@SpringBootApplication | ||
public class AzureAdIntegrationSpringBootAutoconfigureSampleApplication { | ||
|
||
public static void main(String[] args) { | ||
SpringApplication.run(AzureAdIntegrationSpringBootAutoconfigureSampleApplication.class, args); | ||
} | ||
} |
101 changes: 101 additions & 0 deletions
101
...le/src/main/java/com/microsoft/azure/autoconfigure/aad/controller/TodolistController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
/** | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. See LICENSE in the project root for | ||
* license information. | ||
*/ | ||
package com.microsoft.azure.autoconfigure.aad.controller; | ||
|
||
import com.microsoft.azure.autoconfigure.aad.UserGroup; | ||
import com.microsoft.azure.autoconfigure.aad.UserPrincipal; | ||
import com.microsoft.azure.autoconfigure.aad.model.TodoItem; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.security.access.prepost.PreAuthorize; | ||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; | ||
import org.springframework.web.bind.annotation.*; | ||
|
||
import java.util.*; | ||
import java.util.stream.Collectors; | ||
|
||
@RestController | ||
public class TodolistController { | ||
private final List<TodoItem> todoList = new ArrayList<TodoItem>(); | ||
|
||
public TodolistController() { | ||
todoList.add(0, new TodoItem(2398, "anything", "whoever")); | ||
} | ||
|
||
@RequestMapping("/home") | ||
public Map<String, Object> home() { | ||
final Map<String, Object> model = new HashMap<String, Object>(); | ||
model.put("id", UUID.randomUUID().toString()); | ||
model.put("content", "home"); | ||
return model; | ||
} | ||
|
||
/** | ||
* HTTP GET | ||
*/ | ||
@RequestMapping(value = "/api/todolist/{index}", | ||
method = RequestMethod.GET, produces = {MediaType.APPLICATION_JSON_VALUE}) | ||
public ResponseEntity<?> getTodoItem(@PathVariable("index") int index) { | ||
if (index > todoList.size() - 1) { | ||
return new ResponseEntity<Object>(new TodoItem(-1, "index out of range", null), HttpStatus.NOT_FOUND); | ||
} | ||
return new ResponseEntity<TodoItem>(todoList.get(index), HttpStatus.OK); | ||
} | ||
|
||
/** | ||
* HTTP GET ALL | ||
*/ | ||
@RequestMapping(value = "/api/todolist", method = RequestMethod.GET, produces = {MediaType.APPLICATION_JSON_VALUE}) | ||
public ResponseEntity<List<TodoItem>> getAllTodoItems() { | ||
return new ResponseEntity<List<TodoItem>>(todoList, HttpStatus.OK); | ||
} | ||
|
||
@PreAuthorize("hasRole('ROLE_group1')") | ||
@RequestMapping(value = "/api/todolist", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE) | ||
public ResponseEntity<String> addNewTodoItem(@RequestBody TodoItem item) { | ||
item.setID(todoList.size() + 1); | ||
todoList.add(todoList.size(), item); | ||
return new ResponseEntity<String>("Entity created", HttpStatus.CREATED); | ||
} | ||
|
||
/** | ||
* HTTP PUT | ||
*/ | ||
@PreAuthorize("hasRole('ROLE_group1')") | ||
@RequestMapping(value = "/api/todolist", method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE) | ||
public ResponseEntity<String> updateTodoItem(@RequestBody TodoItem item) { | ||
final List<TodoItem> find = | ||
todoList.stream().filter(i -> i.getID() == item.getID()).collect(Collectors.toList()); | ||
if (!find.isEmpty()) { | ||
todoList.set(todoList.indexOf(find.get(0)), item); | ||
return new ResponseEntity<String>("Entity is updated", HttpStatus.OK); | ||
} | ||
return new ResponseEntity<String>("Entity not found", HttpStatus.OK); | ||
} | ||
|
||
/** | ||
* HTTP DELETE | ||
*/ | ||
@RequestMapping(value = "/api/todolist/{id}", method = RequestMethod.DELETE) | ||
public ResponseEntity<String> deleteTodoItem(@PathVariable("id") int id, | ||
PreAuthenticatedAuthenticationToken authToken) { | ||
final UserPrincipal current = (UserPrincipal) authToken.getPrincipal(); | ||
|
||
if (current.isMemberOf( | ||
new UserGroup("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "group1"))) { | ||
final List<TodoItem> find = todoList.stream().filter(i -> i.getID() == id).collect(Collectors.toList()); | ||
if (!find.isEmpty()) { | ||
todoList.remove(todoList.indexOf(find.get(0))); | ||
return new ResponseEntity<String>("OK", HttpStatus.OK); | ||
} | ||
return new ResponseEntity<String>("Entity not found", HttpStatus.OK); | ||
} else { | ||
return new ResponseEntity<String>("Access is denied", HttpStatus.OK); | ||
} | ||
|
||
} | ||
} |
Oops, something went wrong.