5

我对 OAuth2 真的很陌生,并试图在角色 auth.server 中构建一台服务器来授权用户和一个保留受保护资源的服务器......

我遇到了需要使用 ResourceServerConfigurerAdapter 保护的问题。似乎他忽略了从 userInfoUrl 中获取的所有角色...

所以这里的代码:

认证服务器

@SpringBootApplication
@EnableAuthorizationServer
@EnableResourceServer
@RestController
public class Oa2AuthServerApplication {

    @RequestMapping("/user")
    public Principal user(Principal user) {
        return user;
    }
    public static void main(String[] args) {
        SpringApplication.run(Oa2AuthServerApplication.class, args);
    }
}

__

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("admin")
                .password("admin")
                .roles("ADMIN", "USER")
                .and()
                .withUser("user")
                .password("user")
                .roles("USER");
    }
}

__

@Configuration
public class OA2AuthConfig extends AuthorizationServerConfigurerAdapter {
    @Autowired
    private AuthenticationManager authenticationManager;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager);
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("default")
                .secret("kx")
                .scopes("AUTH", "TRUST")
                .autoApprove(true)
                .authorities("ROLE_GUEST", "ROLE_USER", "ROLE_ADMIN")
                .authorizedGrantTypes("authorization_code", "implicit", "refresh_token");
    }
}

资源服务器

@SpringBootApplication
@RestController
@EnableResourceServer
public class Oa2ResourceServerApplication {
    @RequestMapping("/")
    public String greet() {
        return UUID.randomUUID().toString() + "\r\n";
    }

    @RequestMapping("/forAdmin")
    public String admin() {
        return "hi admin!";
    }


    public static void main(String[] args) {
        SpringApplication.run(Oa2ResourceServerApplication.class, args);
    }
}

因此,从 authserver 获取令牌 + 调用“localhost:9091/”和“/forAdmin”可以使用此令牌。

但是当我这样做时:

public class WebSecurityConfig extends ResourceServerConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/forAdmin").hasRole("USER");
    }

我被拒绝访问....

可以肯定的是,角色正在到达资源服务器,我已将 geet() 从上面更改为

@RequestMapping("/")
    public String greet(Principal user) {
        if (user instanceof OAuth2Authentication) {
            log.info("having roles: {}", ((OAuth2Authentication) user).getAuthorities());
        }
        return UUID.randomUUID().toString() + "\r\n";
    }

控制台显示

dkauth.Oa2ResourceServerApplication :具有角色:[{authority=ROLE_USER}]

因此,当“Principal”是当前经过身份验证的用户时,我假设 resourceserverer 配置器存在错误......或者我正在做一些致命的错误......

或两者兼而有之....我不知道

有人可以帮助我解决这个问题吗?

4

6 回答 6

4

所以JWT是必须的,没有它就不行。

我用以下组合解决了它:

@PreAuthorize("#oauth2.hasScope('openid') and hasRole('ROLE_ADMIN')")

您可以在此处找到受保护资源的示例。

于 2017-01-08T17:47:48.657 回答
2

将客户端连接到我的 AuthServer 时,我遇到了类似的问题。我发现当客户端解析从服务器提供的角色时,它使用 AuthoritiesExtractor。默认使用的是 FixedAuthoritiesExtractor。

FixedAuthoritiesExtractor 中的代码具有将具有权限的 Map 转换为 GrantedAuthority 列表的方法,并且作为其中的一部分,它在称为 asAuthorities 的方法中获取角色的名称。

private String asAuthorities(Object object) {
    if (object instanceof Collection) {
        return StringUtils.collectionToCommaDelimitedString((Collection<?>) object);
    }
    if (ObjectUtils.isArray(object)) {
        return StringUtils.arrayToCommaDelimitedString((Object[]) object);
    }
    return object.toString();
}

调试时,我可以看到进来的对象是一个列表,但该列表的内容是一个地图。所以它是一个List<Map<String,String>>. 在地图内部,它包含一个具有关键权限和价值角色的条目。

假设我们ROLE_USER在 AuthServer 上有这个角色。在地图对象上使用 toString 时,它将 this 转换为 String {authority=ROLE_USER}。如果您检查用户现在是否包含角色名称ROLE_USER,它将不等于名称{authority=ROLE_USER}

因此,我创建了 AuthoritiesExtractor 的新版本。

public class OAuth2AuthoritiesExtractor implements AuthoritiesExtractor {

    static final String AUTHORITIES = "authorities";

    @Override
    public List<GrantedAuthority> extractAuthorities(Map<String, Object> map) {
        String authorities = "ROLE_USER";
        if (map.containsKey(AUTHORITIES)) {
            authorities = asAuthorities(map.get(AUTHORITIES));
        }
        return AuthorityUtils.commaSeparatedStringToAuthorityList(authorities);
    }

    @SuppressWarnings("unchecked")
    private String asAuthorities(Object object) {
        if (object instanceof Collection) {
            return (String) ((Collection) object).stream().map(o -> {
                if (o instanceof Map) {
                    return ((Map) o).values().stream().collect(Collectors.joining(","));
                }
                return o.toString();
            }).collect(Collectors.joining(","));

        }
        if (ObjectUtils.isArray(object)) {
            return StringUtils.arrayToCommaDelimitedString((Object[]) object);
        }
        return object.toString();
    }

}

使用此提取器时,它会检测到该集合包含一个 Map - 如果包含,它将使用该映射中的值作为角色名称。

我现在在 spring security 中获得的角色被剥离了{authority=部分,现在只包含 String ROLE_USER,并且检查isUserInRole现在是否有效。

于 2016-06-21T10:52:14.527 回答
2

我认为您缺少角色前缀。

从 Spring Security 4.x 开始,角色必须加上前缀,例如,如果你正在做

.antMatchers("/forAdmin").hasRole("USER");

您必须将其更改为:

.antMatchers("/forAdmin").hasRole("ROLE_USER");

角色由 RoleVoter 处理,并且具有前缀让投票者知道哪些令牌是角色名称,因此它可以忽略它无法处理的那些。例如,您可以指定“ROLE_ADMIN,IS_AUTHENTICATED_FULLY”,但您不希望该选民处理 IS_AUTHENTICATED_FULLY - AuthenticatedVoter 应该处理它。

从官方文档

如果任何 ConfigAttribute.getAttribute() 以表明它是角色的前缀开头,则投票。默认前缀字符串是 ROLE_,但可以将其覆盖为任何值。它也可以设置为空,这意味着基本上任何属性都将被投票。如下文进一步描述的,空前缀的效果可能不是很理想。

于 2016-01-29T16:52:26.000 回答
1

问题是,通过 userInfoUri 进行的令牌交换无法正常工作。您可以保护资源服务器免受未经授权的访问,但 HttpSecurity 配置中的 access() 方法似乎总是拒绝请求。

添加 JWT 令牌存储解决了这个问题。

我在这里的博客文章中对此进行了更详细的解释:stytex.de/blog/2016/02/01/spring-cloud-security-with-oauth2/

于 2016-02-07T14:00:40.940 回答
0

我的端点如下所示:

    @ApiImplicitParams({
            @ApiImplicitParam(name = "Authorization", value = "Authorization token",
                    required = true, dataType = "string", paramType = "header") })
    @PreAuthorize("hasRole('ROLE_ADMIN')")
    @PutMapping
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void updateStory(@RequestBody StoryDTO story) {

        LOGGER.info("Updating story with title: {}", story.getTitle());
        storyService.updateStory(story);
    }

我努力了

@PreAuthorize("#oauth2.clientHasRole('ROLE_ADMIN')") 

但只对我有用

@PreAuthorize("hasRole('ROLE_ADMIN')")

您可能缺少此配置:

@Order(1)
@EnableWebSecurity // THIS !!!
@EnableGlobalMethodSecurity(prePostEnabled = true) // THIS !!!
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("admin").password(passwordEncoder().encode(ADMIN_PASSWORD)).roles("ADMIN").and()
                .withUser("user").password(passwordEncoder().encode(USER_PASSWORD)).roles("USER");
    }
}

我使用 Spring Boot 2 - 2.1.9.RELEASE

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.security.oauth.boot</groupId>
            <artifactId>spring-security-oauth2-autoconfigure</artifactId>
            <version>2.2.2.RELEASE</version>
        </dependency>
    </dependencies>

希望这可以帮助。

编辑 如果你想使用 @PreAuthorize("#oauth2.clientHasRole('ROLE_ADMIN')") 你需要注册OAuth2MethodSecurityExpressionHandler

于 2020-02-02T21:01:50.793 回答
0

就我而言,我必须添加AuthoritiesExtractor实现,然后将其作为 bean 添加到ResourceServerConfigurerAdapter

 public class OAuth2AuthoritiesExtractor implements AuthoritiesExtractor {

    static final String AUTHORITIES = "roles";

    @Override
    public List<GrantedAuthority> extractAuthorities(Map<String, Object> map) {
        ArrayList<String> authorities = new ArrayList<String>();
        if (map.containsKey(AUTHORITIES)) {
            Arrays.stream(((String) map.get(AUTHORITIES)).split(",")).forEach(s -> {
                authorities.add("ROLE_".concat(s.toUpperCase(Locale.ROOT)));
            });
        }
        return AuthorityUtils.commaSeparatedStringToAuthorityList(String.join(",", authorities));
    }
}

然后在实现中添加一个bean ResourceServerConfigurerAdapter

 @Configuration
    public class ResourcesConfiguration extends ResourceServerConfigurerAdapter implements Filter {
    
        @Bean
        public AuthoritiesExtractor githubAuthoritiesExtractor() {
            return new OAuth2AuthoritiesExtractor();
        }
}
于 2021-09-08T16:47:16.063 回答