Spring oauth2已拒绝访问

时间:2016-01-29 16:07:18

标签: spring spring-cloud spring-oauth2

我是OAuth2的新手,并尝试在角色auth.server中构建一个服务器来授权用户,并保留一个受保护的资源......

我遇到了使用ResourceServerConfigurerAdapter保护的问题。看起来他忽略了从userInfoUrl获取所有角色......

所以这里的代码:

AuthServer

@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");
    }
}

ResourceServer

@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";
    }

,控制台显示

d.k.auth.Oa2ResourceServerApplication:具有角色:[{authority = ROLE_USER}]

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

或两者......我不知道

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

5 个答案:

答案 0 :(得分:3)

所以JWT是必要的,没有它不起作用。

我用组合解决了它:

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

您可以找到受保护资源here的示例。

答案 1 :(得分: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。所以它是List<Map<String,String>>。在地图内部,它包含一个具有关键权限和值角色的条目。

假设我们在AuthServer上有角色ROLE_USER。 在地图对象上使用toString时,它会将其转换为字符串{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安全性中获得的角色被剥离了{authority=部分,现在只包含字符串ROLE_USER,并检查isUserInRole现在是否有效。

答案 2 :(得分:1)

我认为你缺少角色前缀。

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

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

您必须将其更改为:

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

角色由RoleVoter处理,并且前缀允许   选民知道哪些令牌是角色名称,所以它可以忽略它不能的角色   处理。例如,您可以指定   “ROLE_ADMIN,IS_AUTHENTICATED_FULLY”,但你不会想要那个选民   处理IS_AUTHENTICATED_FULLY - AuthenticatedVoter应该处理   这一点。

来自官方documentation

  

如果任何ConfigAttribute.getAttribute()以前缀开头,则返回投票   表明它是一个角色。默认前缀字符串是ROLE_,但是   这可能会被覆盖到任何价值。它也可以设置为空,   这意味着基本上任何属性都将被投票。如   在下面进一步描述的情况下,空前缀的效果可能不是   非常可取。

答案 3 :(得分:0)

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

添加JWT令牌存储修复此问题。

我在此处的博客文章中详细解释了这一点:stytex.de/blog/2016/02/01/spring-cloud-security-with-oauth2/

答案 4 :(得分: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