使用具有 spring 安全性的 Azure AD 高级自定义角色进行基于角色的访问
Using Azure AD premium custom roles with spring security for role based access
我创建了一个演示 spring 启动应用程序,我想在其中使用 AD 和 spring security.Looking 在 Azure 文档中使用 AD 身份验证和授权我做了以下操作
package com.myapp.contactdb.contactfinder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@RequestMapping("/directory")
public interface Directory {
@Autowired
@PreAuthorize("hasRole('Users')")
@GetMapping("/contact/{mobile}")
public String getContact(@PathVariable("mobile") Long mobile);
@Autowired
@GetMapping("/contact/data")
public String getData();
}
这是其余的 API 入口点。
我在相应的 Azure AD.And 中创建了组和用户,使用了 azure 文档中指定的组,例如
package com.myapp.contactdb;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import com.microsoft.azure.spring.autoconfigure.aad.AADAppRoleStatelessAuthenticationFilter;
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.oauth2Login()
.userInfoEndpoint()
.oidcUserService(oidcUserService);
}
}
应用属性为
spring.main.banner-mode=off
# create and drop tables and sequences, loads import.sql
#spring.jpa.hibernate.ddl-auto=create-drop
# MySql settings
spring.datasource.url=jdbc:mysql://localhost:3306/xxxx
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL8Dialect
# HikariCP settings
# spring.datasource.hikari.*
spring.datasource.hikari.connection-timeout=60000
spring.datasource.hikari.maximum-pool-size=5
# azure.activedirectory.tenant-id
azure.activedirectory.tenant-id = xxxx
azure.activedirectory.client-id = xxxx
# spring.security.oauth2.client.registration.azure.client-id
spring.security.oauth2.client.registration.azure.client-id = xxxxxxx
# spring.security.oauth2.client.registration.azure.client-secret
spring.security.oauth2.client.registration.azure.client-secret = xxxxxxxx
azure.activedirectory.active-directory-groups = Users
但是我需要授权使用自定义 roles.I 添加了 azure premium AD 免费试用并创建了一个角色,即“操作员”。
然而问题是我用什么 属性 在 app.props 文件中描述它,以及如何让角色反映在 @Preauthorize(hasRole('Operator')) 中。
任何想法或我可能没有看到的任何东西?
如果你想用app角色投射你的应用,请参考以下步骤
该示例使用 app role auth 到 project web api
-
在您的 Azure AD 网络 api 应用程序中定义应用程序角色。请在您的应用程序清单中添加以下内容
"appRoles": [
{
"allowedMemberTypes": [
"User"
],
"displayName": "Admin",
"id": "2fa848d0-8054-4e11-8c73-7af5f1171001",
"isEnabled": true,
"description": "Full admin access",
"value": "Admin"
},
{
"allowedMemberTypes": [
"User"
],
"displayName": "User",
"id": "f8ed78b5-fabc-488e-968b-baa48a570001",
"isEnabled": true,
"description": "Normal user access",
"value": "User"
}
],
-
Register client application in Azure AD and configure API permissions
在客户端应用程序中启用隐式流
配置API应用程序
一个。 SDK
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-active-directory-spring-boot-starter</artifactId>
<version>2.3.1</version>
</dependency>
b。 application.properties
azure.activedirectory.session-stateless=true
azure.activedirectory.client-id=xxxxxx-your-client-id-xxxxxx
azure.activedirectory.appIdUri=xxxxxx-your-appIDUri-xxxxxx
c。 Web 安全配置 class
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private AADAppRoleStatelessAuthenticationFilter aadAuthFilter;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER);
http.authorizeRequests()
.antMatchers("/", "/index.html", "/public").permitAll()
.anyRequest().authenticated();
http.addFilterBefore(aadAuthFilter, UsernamePasswordAuthenticationFilter.class);
}
}
d。控制器
@RestController
public class HelloController {
@GetMapping("/public")
@ResponseBody
public String publicMethod() {
return "public endpoint response";
}
@GetMapping("/authorized")
@ResponseBody
@PreAuthorize("hasAnyRole('User','Admin')")
public String onlyAuthorizedUsers() {
return "authorized endpoint response";
}
@GetMapping("/admin/demo")
@PreAuthorize("hasRole('Admin')")
@ResponseBody
public String onlyForAdmins() {
return "admin endpoint";
}
}
- 测试。我用单页做测试
@吉姆,
所以最后我通过修改上面问题
中的WebSecurityConfig class来解决这个问题
package com.xxx.contactdb;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority;
import net.minidev.json.JSONArray;
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().and().oauth2Login().userInfoEndpoint()
.oidcUserService(this.oidcUserService());
}
/**
* Replaces the granted authorities value received in token with the roles value
* in token received from the app roles attribute defined in manifest and
* creates a new OIDCUser with updated mappedAuthorities
*
* @return oidcUser
*/
private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {
final OidcUserService delegate = new OidcUserService();
return (userRequest) -> {
Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
// Delegate to the default implementation for loading a user
OidcUser oidcUser = delegate.loadUser(userRequest);
oidcUser.getAuthorities().forEach(authority -> {
if (OidcUserAuthority.class.isInstance(authority)) {
OidcUserAuthority oidcUserAuthority = (OidcUserAuthority) authority;
Map<String, Object> userInfo = oidcUserAuthority.getAttributes();
JSONArray roles = null;
if (userInfo.containsKey("roles")) {
try {
roles = (JSONArray) userInfo.get("roles");
roles.forEach(s -> {
mappedAuthorities.add(new SimpleGrantedAuthority("ROLE_" + (String) s));
});
} catch (Exception e) {
// Replace this with logger during implementation
e.printStackTrace();
}
}
}
});
oidcUser = new DefaultOidcUser(mappedAuthorities, oidcUser.getIdToken(), oidcUser.getUserInfo());
return oidcUser;
};
}
}
我为使用 Azure 2.3.1 和 spring 安全版本 5.3.3 的 spring 引导版本 2.3.1 版本做了此更改。
提到这一点是因为对于 Spring 引导版本 2.1.13,我们可以使用 UserAuthoritiesMapping,因为当局将具有 OIDCUserService 类型映射,而最新的没有。
但是,如果使用 DB 将角色填充到授予权限,那么他们仍然可以使用此选项,而不是 OidcUser option.This 目前正在工作。
我创建了一个演示 spring 启动应用程序,我想在其中使用 AD 和 spring security.Looking 在 Azure 文档中使用 AD 身份验证和授权我做了以下操作
package com.myapp.contactdb.contactfinder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@RequestMapping("/directory")
public interface Directory {
@Autowired
@PreAuthorize("hasRole('Users')")
@GetMapping("/contact/{mobile}")
public String getContact(@PathVariable("mobile") Long mobile);
@Autowired
@GetMapping("/contact/data")
public String getData();
}
这是其余的 API 入口点。 我在相应的 Azure AD.And 中创建了组和用户,使用了 azure 文档中指定的组,例如
package com.myapp.contactdb;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import com.microsoft.azure.spring.autoconfigure.aad.AADAppRoleStatelessAuthenticationFilter;
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.oauth2Login()
.userInfoEndpoint()
.oidcUserService(oidcUserService);
}
}
应用属性为
spring.main.banner-mode=off
# create and drop tables and sequences, loads import.sql
#spring.jpa.hibernate.ddl-auto=create-drop
# MySql settings
spring.datasource.url=jdbc:mysql://localhost:3306/xxxx
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL8Dialect
# HikariCP settings
# spring.datasource.hikari.*
spring.datasource.hikari.connection-timeout=60000
spring.datasource.hikari.maximum-pool-size=5
# azure.activedirectory.tenant-id
azure.activedirectory.tenant-id = xxxx
azure.activedirectory.client-id = xxxx
# spring.security.oauth2.client.registration.azure.client-id
spring.security.oauth2.client.registration.azure.client-id = xxxxxxx
# spring.security.oauth2.client.registration.azure.client-secret
spring.security.oauth2.client.registration.azure.client-secret = xxxxxxxx
azure.activedirectory.active-directory-groups = Users
但是我需要授权使用自定义 roles.I 添加了 azure premium AD 免费试用并创建了一个角色,即“操作员”。 然而问题是我用什么 属性 在 app.props 文件中描述它,以及如何让角色反映在 @Preauthorize(hasRole('Operator')) 中。 任何想法或我可能没有看到的任何东西?
如果你想用app角色投射你的应用,请参考以下步骤
该示例使用 app role auth 到 project web api
在您的 Azure AD 网络 api 应用程序中定义应用程序角色。请在您的应用程序清单中添加以下内容
"appRoles": [
{
"allowedMemberTypes": [
"User"
],
"displayName": "Admin",
"id": "2fa848d0-8054-4e11-8c73-7af5f1171001",
"isEnabled": true,
"description": "Full admin access",
"value": "Admin"
},
{
"allowedMemberTypes": [
"User"
],
"displayName": "User",
"id": "f8ed78b5-fabc-488e-968b-baa48a570001",
"isEnabled": true,
"description": "Normal user access",
"value": "User"
}
],
Register client application in Azure AD and configure API permissions
在客户端应用程序中启用隐式流
配置API应用程序
一个。 SDK
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-active-directory-spring-boot-starter</artifactId>
<version>2.3.1</version>
</dependency>
b。 application.properties
azure.activedirectory.session-stateless=true
azure.activedirectory.client-id=xxxxxx-your-client-id-xxxxxx
azure.activedirectory.appIdUri=xxxxxx-your-appIDUri-xxxxxx
c。 Web 安全配置 class
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private AADAppRoleStatelessAuthenticationFilter aadAuthFilter;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER);
http.authorizeRequests()
.antMatchers("/", "/index.html", "/public").permitAll()
.anyRequest().authenticated();
http.addFilterBefore(aadAuthFilter, UsernamePasswordAuthenticationFilter.class);
}
}
d。控制器
@RestController
public class HelloController {
@GetMapping("/public")
@ResponseBody
public String publicMethod() {
return "public endpoint response";
}
@GetMapping("/authorized")
@ResponseBody
@PreAuthorize("hasAnyRole('User','Admin')")
public String onlyAuthorizedUsers() {
return "authorized endpoint response";
}
@GetMapping("/admin/demo")
@PreAuthorize("hasRole('Admin')")
@ResponseBody
public String onlyForAdmins() {
return "admin endpoint";
}
}
- 测试。我用单页做测试
@吉姆,
所以最后我通过修改上面问题
中的WebSecurityConfig class来解决这个问题package com.xxx.contactdb;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority;
import net.minidev.json.JSONArray;
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().and().oauth2Login().userInfoEndpoint()
.oidcUserService(this.oidcUserService());
}
/**
* Replaces the granted authorities value received in token with the roles value
* in token received from the app roles attribute defined in manifest and
* creates a new OIDCUser with updated mappedAuthorities
*
* @return oidcUser
*/
private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {
final OidcUserService delegate = new OidcUserService();
return (userRequest) -> {
Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
// Delegate to the default implementation for loading a user
OidcUser oidcUser = delegate.loadUser(userRequest);
oidcUser.getAuthorities().forEach(authority -> {
if (OidcUserAuthority.class.isInstance(authority)) {
OidcUserAuthority oidcUserAuthority = (OidcUserAuthority) authority;
Map<String, Object> userInfo = oidcUserAuthority.getAttributes();
JSONArray roles = null;
if (userInfo.containsKey("roles")) {
try {
roles = (JSONArray) userInfo.get("roles");
roles.forEach(s -> {
mappedAuthorities.add(new SimpleGrantedAuthority("ROLE_" + (String) s));
});
} catch (Exception e) {
// Replace this with logger during implementation
e.printStackTrace();
}
}
}
});
oidcUser = new DefaultOidcUser(mappedAuthorities, oidcUser.getIdToken(), oidcUser.getUserInfo());
return oidcUser;
};
}
}
我为使用 Azure 2.3.1 和 spring 安全版本 5.3.3 的 spring 引导版本 2.3.1 版本做了此更改。 提到这一点是因为对于 Spring 引导版本 2.1.13,我们可以使用 UserAuthoritiesMapping,因为当局将具有 OIDCUserService 类型映射,而最新的没有。 但是,如果使用 DB 将角色填充到授予权限,那么他们仍然可以使用此选项,而不是 OidcUser option.This 目前正在工作。