Securing Spring backed, when fronted is secured with adal auth

和自甴很熟 提交于 2020-08-05 07:13:19

问题


So we have this application that has two parts

  1. Front end ui - using Angular JS
  2. Back end - rest api using Spring boot

Front end is secured using microsoft-adal-angular6 library to authenticate with Azure Active Directory

My question is what is the right way to secure the Back end so only active directory authenticated users can access the API?


回答1:


I would suggest to use a jwt token, that is attached to every request to your backend as 'Authorization' header. The token consists of three parts, where one is holding data about the user and one a signature, so you can validate that your token was created by a trusted source. The data part can look something like this:

{
    "iss": "Online JWT Builder",
    "iat": 1580283510,
    "exp": 1611819510,
    "aud": "www.example.com",
    "sub": "jrocket@example.com",
    "GivenName": "Johnny",
    "roles": ["PROJECT_MANAGER", "ADMIN"]
    "scope": "WEBAPP"
}

On the spring side, I would suggest using Spring Security 5 with the latest configuration. You will need those dependencies:

         <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>5.x.x.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-oauth2-jose</artifactId>
            <version>5.x.x.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-oauth2-resource-server</artifactId>
            <version>5.x.x.RELEASE</version>

Now you can enable security and configure it with a configuration class. Inside you can define which scope the request must have, how to sign the token and with route should be public or secured.


@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Value("${spring.security.oauth2.resourceserver.jwt.jwk-set-uri}")
    String jwkSetUri;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // @formatter:off
        http
            .cors().disable()
            .authorizeRequests()
            .antMatchers(("/webapp/**")).hasAuthority("SCOPE_WEBAPP")
            .antMatchers(("/admin/**")).hasRole("ADMIN")
            ...
            .and()
            .oauth2ResourceServer().jwt(jwtConfigurer -> jwtConfigurer.decoder(jwtDecoder())
            .jwtAuthenticationConverter(new CustomJwtAuthenticationConverter()))
            ...
        // @formatter:on
    }

    @Bean
    JwtDecoder jwtDecoder() {
        return NimbusJwtDecoder.withJwkSetUri(jwkSetUri).build();
    }
}

I had to use a custom JwtConverter to get the roles from the jwt, but it depends on how you do it, I guess.

public class CustomJwtAuthenticationConverter implements Converter<Jwt, AbstractAuthenticationToken> {

    private final JwtGrantedAuthoritiesConverter defaultGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();

    public CustomJwtAuthenticationConverter() {
    }

    @Override
    public AbstractAuthenticationToken convert(@NotNull final Jwt jwt) {
        Collection<GrantedAuthority> authorities = Stream
                .concat(defaultGrantedAuthoritiesConverter.convert(jwt).stream(), extractResourceRoles(jwt).stream())
                .collect(Collectors.toSet());
        return new JwtAuthenticationToken(jwt, authorities);
    }

    private static Collection<? extends GrantedAuthority> extractResourceRoles(final Jwt jwt) {
        Collection<String> userRoles = jwt.getClaimAsStringList("roles");
        if (userRoles != null)
            return userRoles
                    .stream()
                    .map(role -> new SimpleGrantedAuthority("ROLE_" + role))
                    .collect(Collectors.toSet());
        return Collections.emptySet();
    }
}

This enables you to secure your application on an url basis.

The roles in the jwt, the JwtConverter and the @EnableGlobalMethodSecurity annotation enable you to secure even on a method level.

@Transactional
@PreAuthorize("hasRole('ROLE_PROJECT_MANAGER')")
public Page<Project> findAll(Pageable pageable) {
    return projectRepository.findAll(pageable);
}

Azure Active Directory should support jwt, but I don't have experience with this IDP. What I can't answer is, how you can inject custom claims like roles inside the token and where to get the jwks (Json Web Key Set), which is used to validate the token's signature.



来源:https://stackoverflow.com/questions/59885114/securing-spring-backed-when-fronted-is-secured-with-adal-auth

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!