如何在 Spring Boot OIDC 应用程序的控制器中获取用户详细信息?

我已经修改了 here 中的代码来调用 MitreID OIDC 服务器。

我的控制器:

    public final String home(Principal p) {
    final String username = SecurityContextHolder.getContext().getAuthentication().getName();
...

返回 null 并且对于所有用户详细信息为空。

我也试过:

public final String home(@AuthenticationPrincipal OpenIdConnectUserDetails user) {
        final String username = user.getUsername();

@RequestMapping(value = "/username", method = RequestMethod.GET)
    @ResponseBody
    public String currentUserNameSimple(HttpServletRequest request) {
        Principal principal = request.getUserPrincipal();
        return "username: " + principal.getName();
    }

一切都是空的,但身份验证 is 返回访问和用户令牌。

我的安全配置是:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private OAuth2RestTemplate restTemplate;

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/resources/**");
    }

    @Bean
    public OpenIdConnectFilter myFilter() {

        final OpenIdConnectFilter filter = new OpenIdConnectFilter("/openid_connect_login");
        filter.setRestTemplate(restTemplate);
        return filter;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        // @formatter:off
        http
        .addFilterAfter(new OAuth2ClientContextFilter(), AbstractPreAuthenticatedProcessingFilter.class)
        .addFilterAfter(myFilter(), OAuth2ClientContextFilter.class)
        .httpBasic().authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/openid_connect_login"))
        .and()
        .authorizeRequests()
        .antMatchers("/","/index*").permitAll()
        .anyRequest().authenticated()
        ;

     // @formatter:on
    }
}

那么为什么我的控制器不能访问用户详细信息呢?

编辑:根据要求, OpenIdConnectFilter

 package org.baeldung.security;

import java.io.IOException;
import java.net.URL;
import java.security.interfaces.RSAPublicKey;
import java.util.Date;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.jwt.Jwt;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.security.jwt.crypto.sign.RsaVerifier;
import org.springframework.security.oauth2.client.OAuth2RestOperations;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;

import com.auth0.jwk.Jwk;
import com.auth0.jwk.JwkProvider;
import com.auth0.jwk.UrlJwkProvider;
import com.fasterxml.jackson.databind.ObjectMapper;

public class OpenIdConnectFilter extends AbstractAuthenticationProcessingFilter {
    @Value("${oidc.clientId}")
    private String clientId;

    @Value("${oidc.issuer}")
    private String issuer;

    @Value("${oidc.jwkUrl}")
    private String jwkUrl;

    public OAuth2RestOperations restTemplate;

    public OpenIdConnectFilter(String defaultFilterProcessesUrl) {
        super(defaultFilterProcessesUrl);
        setAuthenticationManager(new NoopAuthenticationManager());
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {

        OAuth2AccessToken accessToken;
        logger.info("ewd here: b " );
        try {
            accessToken = restTemplate.getAccessToken();
        } catch (final OAuth2Exception e) {
            throw new BadCredentialsException("Could not obtain access token", e);
        }
        try {
            logger.info("ewd access token: " + accessToken);
            final String idToken = accessToken.getAdditionalInformation().get("id_token").toString();
            String kid = JwtHelper.headers(idToken)
                .get("kid");
            final Jwt tokenDecoded = JwtHelper.decodeAndVerify(idToken, verifier(kid));
            final Map<String, String> authInfo = new ObjectMapper().readValue(tokenDecoded.getClaims(), Map.class);
            verifyClaims(authInfo);
            final OpenIdConnectUserDetails user = new OpenIdConnectUserDetails(authInfo, accessToken);
            logger.info("ewd user token: " + tokenDecoded);
            return new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
        } catch (final Exception e) {
            throw new BadCredentialsException("Could not obtain user details from token", e);
        }

    }

    public void verifyClaims(Map claims) {
        int exp = (int) claims.get("exp");
        Date expireDate = new Date(exp * 1000L);
        Date now = new Date();
        if (expireDate.before(now) || !claims.get("iss").equals(issuer) || !claims.get("aud").equals(clientId)) {
            throw new RuntimeException("Invalid claims");
        }
    }

    private RsaVerifier verifier(String kid) throws Exception {
        JwkProvider provider = new UrlJwkProvider(new URL(jwkUrl));
        Jwk jwk = provider.get(kid);
        return new RsaVerifier((RSAPublicKey) jwk.getPublicKey());
    }

    public void setRestTemplate(OAuth2RestTemplate restTemplate2) {
        restTemplate = restTemplate2;

    }

    private static class NoopAuthenticationManager implements AuthenticationManager {

        @Override
        public Authentication authenticate(Authentication authentication) throws AuthenticationException {
            throw new UnsupportedOperationException("No authentication should be done with this AuthenticationManager");
        }

    }
}
stack overflow How do I get user details in controller of Spring Boot OIDC app?
原文答案

答案:

作者头像

In the tutorial you refer to, Google OpenId Connect is used. This service also returns the extra scope email variable that is read in:

OpenIdConnectUserDetails.class

    public OpenIdConnectUserDetails(Map<String, String> userInfo, OAuth2AccessToken token) {
        this.userId = userInfo.get("sub");
        this.username = userInfo.get("email");
        this.token = token;
    }

Without knowing the specific configuration of your MitreID OIDC server maybe the openId server is not returning the email variable

作者头像

I am a new auth0 user and had the same issue, trying to get some User information in the Spring Controller. I am able to access the Claims from the token using this code.

@GetMapping
public List<Task> getTasks(AuthenticationJsonWebToken authentication) {
    logger.debug("getTasks called.");
    DecodedJWT jwt = JWT.decode(authentication.getToken());
    Map<String, Claim> claims = jwt.getClaims();
    for (Object key: claims.keySet()) {
        logger.debug("key: {}, value: {}", key.toString(),  claims.get(key).asString());
    }

    return taskRepository.findAll();
}

Hope this helps.

作者头像

If you need username, you can get it from JwtAuthenticationToken object as below:

@GetMapping("/home")
public String home(JwtAuthenticationToken user) {
        String name = user.getName();

If you need some other information from user's profile, you can call your auth server's /userinfo endpoint with the access token as below: This will fetch info only if you had included profile scope in your authorize call.

@GetMapping("/home")
public String home(JwtAuthenticationToken user) {

        HttpHeaders headers = new HttpHeaders();
        headers.add(HttpHeaders.AUTHORIZATION, "Bearer "+user.getToken().getTokenValue());
        HttpEntity entity = new HttpEntity(headers);
        ResponseEntity<Map> userinfo = template.exchange("https://your-auth-server/default/v1/userinfo", HttpMethod.GET, entity, Map.class);
        String name = (String) userinfo.getBody().get("given_name");

You can retrieve all profile attributes from this response.

作者头像

对于 Auth0,我可以通过两种方式获取用户信息:

1.第一个是直接在控制器上使用JwtAuthenticationToken,如下所示。

@GetMapping("/info") public void users(JwtAuthenticationToken token) { System.out.println(token.getName()); System.out.println(token.getTokenAttributes().get("name")); }

在这里,token.getName() 打印来自 auth0 的用户 ID,并且 token.getTokenAttributes() 返回一个映射,我们可以从中检索我们需要的任何信息。例如打印用户名,我们可以使用 token.getTokenAttributes().get("name")。

  1. 将 Authentication 对象转换为 JwtAuthenticationToken:

    身份验证身份验证 = SecurityContextHolder.getContext().getAuthentication(); JwtAuthenticationToken token = (JwtAuthenticationToken) 认证;

    字符串用户名 = token.getTokenAttributes().get("name");

作者头像

you can retrieve the user or profile related properties that were defined when creating the okta oidc application through the OidcUser class, which can be used with the AuthenticationPrincipal annotation.Follow below steps

**My Controller:**

 @GetMapping("/user")
    public User user(@AuthenticationPrincipal OidcUser oidcUser) {
        System.out.println("oidcUser :: " + oidcUser + "\n\n");

        User user = new User();

        System.out.println("Attributes :: " + oidcUser.getAttributes() + "\n\n");       
        user.setFirstName(oidcUser.getAttribute("given_name"));
        user.setLastName(oidcUser.getAttribute("family_name"));
        user.setName(oidcUser.getAttribute("name"));
        user.setPreferred_username(oidcUser.getAttribute("preferred_username"));
        user.setEmail(oidcUser.getAttribute("email"));
        user.setGroups(getGroupsFromCurrentUser());

        System.out.println(user.toString() + "\n\n");
        return  user;
    }
     
    private List<String> getGroupsFromCurrentUser() {
          List<String> groups = new ArrayList<>();
          Collection<? extends GrantedAuthority> authorities = SecurityContextHolder.getContext().getAuthentication().getAuthorities();
          System.out.println("\n\n"+authorities+"\n\n");
          for (GrantedAuthority auth : authorities) {
              groups.add(auth.getAuthority());
          }
          
          System.out.println("\n\n"+"groups :: "+groups+"\n\n");
          return groups;
        }

相关问题