使用具有 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

  1. Register web API application and configure API scope

  2. 在您的 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"
    }
  ],
  1. Assign these roles for the user

  2. Register client application in Azure AD and configure API permissions

  3. 在客户端应用程序中启用隐式流

  4. 配置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";
    }
}
  1. 测试。我用单页做测试

详情请参考here and here

@吉姆,

所以最后我通过修改上面问题

中的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 目前正在工作。