来自授权服务器的Spring Security-主体与资源服务器不同
我在授权服务器中创建了userinfo
终结点。
@GetMapping("/userinfo")
public Principal me(Principal principal) {
return principal;
}
它返回以下JSON:
{
...
"userAuthentication": {
...
"principal": {
"id": 2,
"username": "xyz",
"password": "......",
"accountNonExpired": true,
"accountNonLocked": true,
"credentialsNonExpired": true,
"enabled": true,
"authorities": [
{
"authority": "ROLE_DONOR"
}
],
"createdAt": "2019-11-08T20:50:46"
},
...
"name": "xyz"
},
...
"principal": {
"id": 2,
"username": "xyz",
"password": "......",
"accountNonExpired": true,
"accountNonLocked": true,
"credentialsNonExpired": true,
"enabled": true,
"authorities": [
{
"authority": "ROLE_DONOR"
}
],
"createdAt": "2019-11-08T20:50:46"
},
...
"name": "xyz"
}
在我的资源服务器用户服务API中,我尝试了sysout
Principal
的值,只是为了查看它的值:
@GetMapping("/{id}")
public ResourceResponseDto findById(@PathVariable("id") long id, Principal principal) throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
String x = objectMapper.writeValueAsString(principal);
System.out.println(x);
return ...;
}
principal
的值不同。其取值相当于上述principal.username
,省略了其他字段:
{
"userAuthentication": {
...
"principal": "xyz",
...
"name": "xyz"
},
...
"principal": "xyz",
...
"name": "xyz"
}
这是如何发生的?
我需要获取id
的值,但它不见了。principal
对象的字段已消失。这会导致我的其他方法出错:
@GetMapping("/{id}")
@PreAuthorize("hasRole('ADMIN') or #id == principal.id")
public ResourceResponseDto findById(@PathVariable("id") long id) {
//
}
我收到此错误;
Failed to evaluate expression 'hasRole('ADMIN') or #id == principal.id'
请帮帮忙。谢谢。
解决方案
可能很晚了,但这是我的解决方案。 因为我在我的资源服务器中使用了UserInfoTokenServices,所以我发现它使用了FixedEpalExtractor,在这个类中我看到了以下内容:
private static final String[] PRINCIPAL_KEYS = new String[] { "user", "username",
"userid", "user_id", "login", "id", "name" };
@Override
public Object extractPrincipal(Map<String, Object> map) {
for (String key : PRINCIPAL_KEYS) {
if (map.containsKey(key)) {
return map.get(key);
}
}
return null;
}
所以它直接返回应用程序中的‘USER’值‘xyz’。 因此,我创建了一个类来实现ArchalExtractor,并重写他俩的方法:
import java.util.Map;
import org.springframework.boot.autoconfigure.security.oauth2.resource.PrincipalExtractor;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSON;
import com.project.LoginUser;
@Component
public class CustomPrincipalExtractor implements PrincipalExtractor {
@Override
@SuppressWarnings("unchecked")
public Object extractPrincipal(Map<String, Object> map) {
Map<String, Object> principal = (Map<String, Object>) map.get("principal");
return JSON.parseObject(JSON.toJSONString(principal), LoginUser.class);
}
}
此处的参数映射类似于授权服务器中的主体,因此我的LoginUser类具有‘main’键,它实现了UserDetail,并添加了一些附加信息,如id、电子邮件...就像你的校长一样。在这里,我使用fast json来解析它,您也可以使用ObjectMapper。 然后在您的资源服务器中定义UserInfoTokenServices Bean。以下是我的代码:
@EnableResourceServer
@EnableWebSecurity
@RequiredArgsConstructor
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
private final ResourceServerProperties sso;
private final CustomPrincipalExtractor customPrincipalExtractor;
@Primary
@Bean
public UserInfoTokenServices tokenService() {
UserInfoTokenServices userInfoTokenServices = new UserInfoTokenServices(sso.getUserInfoUri(), sso.getClientId());
userInfoTokenServices.setPrincipalExtractor(customPrincipalExtractor);
return userInfoTokenServices;
}
@Override
public void configure(HttpSecurity http) throws Exception {
...
}
}
在这里,我创建了UserInfoTokenServices,并设置了我的自定义ArchalExtractor。不要忘记添加这些属性:
security.oauth2.resource.user-info-uri = http://domain:port/your/user-info-url
security.oauth2.resource.prefer-token-info = false
现在您可以在资源服务器中获取您自己的主体对象。
相关文章