Spring Security - Principal from Authorization Server is different from Resource Server

让人想犯罪 __ 提交于 2020-01-05 04:42:11

问题


I created userinfo endpoint in my authorization server.

@GetMapping("/userinfo")
public Principal me(Principal principal) {
    return principal;
}

It returns this 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"
}

In one of my Resource Servers, User Service API, I tried to sysout the value of Principal just to see its value:

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

The value of principal is different. Its value is equivalent to above principal.username, leaving out other fields:

{
    "userAuthentication": {
        ...
        "principal": "xyz",
        ...
        "name": "xyz"
    },
    ...
    "principal": "xyz",
    ...
    "name": "xyz"
}

How does that happen?

I need to get the value of id but it's gone. The fields of principalobject are gone. That causes I think error for my other method:

@GetMapping("/{id}")
@PreAuthorize("hasRole('ADMIN') or #id == principal.id")
public ResourceResponseDto findById(@PathVariable("id") long id) {
    //
}

I'm getting this error;

Failed to evaluate expression 'hasRole('ADMIN') or #id == principal.id'

Please help. Thanks.


回答1:


As I understand, you are implementing OAuth2 Spring Authentication Server. Principle is just an interface that declares the method getName(). For authentication and resource servers principle is implemented by the class OAuth2Authentication. method getPrinciple():

public Object getPrincipal() {
    return this.userAuthentication == null ? this.storedRequest.getClientId() : this.userAuthentication.getPrincipal();
}

If OAuth2 client is being authenticated, it does not have an userAuthentication, just clientId. If user is being authenticated, you see full userAuthentication object.




回答2:


from the Authentication.getPrincipal() documentation:

The identity of the principal being authenticated. In the case of an authentication request with username and password, this would be the username. Callers are expected to populate the principal for an authentication request. The AuthenticationManager implementation will often return an Authentication containing richer information as the principal for use by the application. Many of the authentication providers will create a UserDetails object as the principal.

So, you are in charge of populating the Principal on the authentication process.

One of the way to pass additional info to the Principal, is by extending the UsernamePasswordAuthenticationFilter and overriding the 'attemptAuthentication()' method:

@Override
public Authentication attemptAuthentication(HttpServletRequest request, 
                 HttpServletResponse response) {
    String username = obtainUsername(request);
    String password = obtainPassword(request);

    UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
    setDetails(request, authRequest);

    Authentication authenticated = this.getAuthenticationManager().authenticate(authRequest);

    return new UsernamePasswordAuthenticationToken(
            new YourObjectForPrincipal(...),
            authenticated.getCredentials(), authenticated.getAuthorities());
}

Note passing the YourObjectForPrincipal object, that could contain any data you want, as the first parameter to the UsernamePasswordAuthenticationToken. To get your extended user, just cast to the desired object:

(YourObjectForPrincipal)authentication.getPrincipal();

It's might be not the best solution, but it worked for me. I hope it will help.



来源:https://stackoverflow.com/questions/59391630/spring-security-principal-from-authorization-server-is-different-from-resource

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!