我是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配置程序存在错误....或者我正在做一些致命的错误......
或两者......我不知道
有人可以帮我解决这个问题吗?
答案 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