spring-boot - 如何从 Keycloak 保护的 Spring Boot 应用程序中获取 Principal

标签 spring-boot spring-security keycloak

我正在尝试修改我在不同地方在线找到的 Keycloak 和 Spring Boot 教程中的代码(代码看起来已重复)。无论如何,尽管 Keycloak 保护了正确的 URL 并且 Spring Security 提供了便利,但在尝试检索 Principal 时,它返回为 null。这是我的 application.properties 文件:

keycloak.auth-server-url=http://localhost:9090/auth
keycloak.realm=<some realm>
keycloak.resource=<some resource>
keycloak.ssl-required=external
keycloak.public-client=true
keycloak.principal-attribute=preferred_username
keycloak.use-resource-role-mappings=true
server.port=8100

这是我的 SecurityConfig.java 文件(来自教程的原始文件):
package com.bme.keycloakdemo.configuration;

import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver;
import org.keycloak.adapters.springsecurity.KeycloakSecurityComponents;
import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider;
import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;

@Configuration
@EnableWebSecurity
@ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
    // Submits the KeycloakAuthenticationProvider to the AuthenticationManager
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
        keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
        auth.authenticationProvider(keycloakAuthenticationProvider);
    }

    @Bean
    public KeycloakSpringBootConfigResolver KeycloakConfigResolver() {
        return new KeycloakSpringBootConfigResolver();
    }

    // Specifies the session authentication strategy
    @Bean
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http.authorizeRequests()
            .antMatchers("/demo*")
            .hasRole("user")
            .anyRequest()
            .permitAll();
    }
}

我的网络 Controller 如下:
package com.bme.keycloakdemo.controllers;

import java.security.Principal;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/demo")
public class DemoController {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @GetMapping(value = "/first", produces = {MediaType.APPLICATION_JSON_VALUE})
    public String first(Principal principal) {
        logger.info("WE GOT HERE");
        String retval = "WE GOT HERE! ";
        if (principal != null) {
            retval += principal.toString();
        }
        else {
            retval += "PRINCIPAL IS NULL";
        }
        return retval;
    }

    @GetMapping(value = "/second", produces = {MediaType.APPLICATION_JSON_VALUE})
    public String second(Principal principal) {
        logger.info("WE GOT HERE");
        String retval = "WE GOT HERE! " + principal.getName();
        return retval;
    }

    @GetMapping(value = "/third", produces = {MediaType.APPLICATION_JSON_VALUE})
    public String third(HttpServletRequest request) {
        logger.info("WE GOT HERE");
        Principal principal = request.getUserPrincipal();
        String retval = "WE GOT HERE! ";
        if (principal != null) {
            retval += principal.toString();
        }
        else {
            retval += "PRINCIPAL IS NULL";
        }
        return retval;
    }
}

最后,这是我的 Maven 配置,以防它有所作为:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.batman-evolution</groupId>
    <artifactId>keycloakjournal</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>keycloakjournal</name>
    <description>Journal Secured with Keycloak</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.16.RELEASE</version>
        <relativePath/>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <keycloak.version>3.4.0.Final</keycloak.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.keycloak</groupId>
            <artifactId>keycloak-spring-boot-starter</artifactId>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.keycloak.bom</groupId>
                <artifactId>keycloak-adapter-bom</artifactId>
                <version>${keycloak.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

所以回顾一下这个问题,我希望从 Keycloak 获得 Principal。我目前正在使用 Spring Security 来返回 Principal,但只要 Principal 可用,我真的不关心一种或另一种方式。谢谢你的帮助!

最佳答案

这对我有用:

@GetMapping("/details")
public MyUserDetails getUserDetails(Principal principal) {

    KeycloakAuthenticationToken kp = (KeycloakAuthenticationToken) principal;

    SimpleKeycloakAccount simpleKeycloakAccount = (SimpleKeycloakAccount) kp.getDetails();

    AccesToken token  = simpleKeycloakAccount.getKeycloakSecurityContext().getToken();
    
    return  new MyUserDetails(token.getGivenName(), token.getFamilyName(), token.getEmail());
}

关于spring-boot - 如何从 Keycloak 保护的 Spring Boot 应用程序中获取 Principal,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53586558/

相关文章:

java - 如何为Spring Boot指定外部YAML配置文件

java - Rest 服务始终使用 Postman 返回错误 401,但集成测试正常

java - 在 Spring Security 中的 FORM_LOGIN_FILTER 之后添加可选的身份验证步骤

Java 异常和 HTTP 500 错误

java - 在 XML 配置中引用 JavaConfig 时获取 NoSuchBeanDefinitionException

java - 如何将 'spring boot 2.1 with elasticsearch 6.6' 与集群节点 'https' 连接?

grails - 如何从 spring-security 获取明文密码?

java - Spring Security KeyCloak 适配器不重定向到登录

node.js - 从 NodeJS 中的 keycloak session 获取用户名

keycloak - 如何使用 keycloak rest-api 将领域管理员添加到用户