本文介紹了來自授權服務器的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
現在您可以在資源服務器中獲取您自己的主體對象。
這篇關于來自授權服務器的Spring Security-主體與資源服務器不同的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,