iOS Developer的全栈之路 - Keycloak(4)

本节将介绍如何基于SpringSe使用Keycloak来保护rest api,网上相关的文章很少,几乎没有直接可参考的,这里附上示例代码。其中使用的Keycloak版本为8.0.1。

配置Keycloak

根据系列文章的前两篇已经对Keycloak有了大致的认识,基础配置就不再赘述,这里需要创建一个realm:springboot-integration,并在此realm中创建两个client,一个是spring-security,用于获取token;一个是springboot-rest-api,也就是我们要保护的api。
分别看一下它们的配置:


spring-security.png
rest-api.png

为什么是两个client呢?若要保护的是rest api,需要在访问时带上token,这种行为需要Access Type设置为bearer-only,而设置为bearer-only的client是无法通过请求获取token的。

添加用户

在配置好client之后,在这个realm中添加两个角色(user, admin),并为这两个角色分别添加一个用户,添加的方式可以参考第二篇文章。

Tips: 一个角色可以包含另一个角色的权限,在添加角色时开启Composite Roles,再在下方选择其他角色即可。

获取token

获取token使用的是spring-security这个client,我们在Access Type中选择了credential,此时发送请求获取token时需要带一个client_secret字段,这个字段在此获取:


spring-security-credentials.png

此时便可以通过Postman来获取token了:


request token.png

Spring Boot Application

接下来就需要准备一个简单的springboot项目,源码可参考此连接,在pom文件中添加对Keycloak的依赖:



    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        2.2.2.RELEASE
         
    
    com.example
    keycloak-integration
    0.0.1-SNAPSHOT
    keycloak-integration
    Demo project for Spring Boot

    
        1.8
    

    
        
            org.springframework.boot
            spring-boot-starter-security
        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.keycloak
            keycloak-spring-boot-starter
        
        
            org.projectlombok
            lombok
            true
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
            
                
                    org.junit.vintage
                    junit-vintage-engine
                
            
        
        
            org.springframework.security
            spring-security-test
            test
        
    

    
        
            
                org.keycloak.bom
                keycloak-adapter-bom
                8.0.1
                pom
                import
            
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    


和上篇中的pom相比,添加了keycloak-spring-boot-starter和keycloak-adapter-bom。在此添加了两个endpoint用于演示:

@RestController
public class HelloController {
    @GetMapping(value = "/admin")
    public String admin() {
        return "Admin";
    }
    @GetMapping("/user")
    public String user() {
        return "User";
    }
}

添加了Keycloak的依赖后,剩下的工作就是配置了,默认情况下,Keycloak会从keycloak.json文件中获取配置信息,但在springboot中通常是使用application.properties来配置,所以需要声明一个resolver来覆盖原有的配置文件,添加后,启动后便会从application.properties来获取Keycloak的配置:

@Configuration
public class KeycloakConfig {
    @Bean
    public KeycloakSpringBootConfigResolver keycloakConfigResolver() {
        return new KeycloakSpringBootConfigResolver();
    }
}

配置如下所示:

server.port=8099
keycloak.realm=springboot-integration
keycloak.bearer-only=true
keycloak.auth-server-url=http://localhost:8080/auth
keycloak.ssl-required=external
keycloak.resource=springboot-rest-api
keycloak.use-resource-role-mappings=false
keycloak.principal-attribute=preferred_username
keycloak.credentials.secret=8e4c5bac-8dcb-4f68-a529-c998e19f0c4d
keycloak.confidential-port=0

其中的credentials.secret是从springboot-rest-api这个client中获得的:


rest-api-credentials.png

剩下的配置就是SpringSecurity的配置了:

@KeycloakConfiguration
public class KeycloakSecurityConfigurer extends KeycloakWebSecurityConfigurerAdapter {

    @Bean
    public FilterRegistrationBean keycloakAuthenticationProcessingFilterRegistrationBean(
            KeycloakAuthenticationProcessingFilter filter) {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
        registrationBean.setEnabled(false);
        return registrationBean;
    }

    @Bean
    public FilterRegistrationBean keycloakPreAuthActionsFilterRegistrationBean(
            KeycloakPreAuthActionsFilter filter) {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
        registrationBean.setEnabled(false);
        return registrationBean;
    }

    @Bean
    public FilterRegistrationBean keycloakAuthenticatedActionsFilterBean(
            KeycloakAuthenticatedActionsFilter filter) {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
        registrationBean.setEnabled(false);
        return registrationBean;
    }

    @Bean
    public FilterRegistrationBean keycloakSecurityContextRequestFilterBean(
            KeycloakSecurityContextRequestFilter filter) {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
        registrationBean.setEnabled(false);
        return registrationBean;
    }

    @Bean
    @Override
    @ConditionalOnMissingBean(HttpSessionManager.class)
    protected HttpSessionManager httpSessionManager() {
        return new HttpSessionManager();
    }

    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new NullAuthenticatedSessionStrategy();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        SimpleAuthorityMapper grantedAuthorityMapper = new SimpleAuthorityMapper();
        grantedAuthorityMapper.setConvertToUpperCase(true);

        KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
        keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(grantedAuthorityMapper);

        auth.authenticationProvider(keycloakAuthenticationProvider);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        super.configure(http);
        http.cors().and().csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers("/user").hasRole("USER")
                .antMatchers("/admin").hasRole("ADMIN")
                .anyRequest().permitAll();
    }
}

此处的父类KeycloakWebSecurityConfigurerAdapter,它继承了WebSecurityConfigurerAdapter,也就是我们之前配置SpringSecurity的配置类,而@KeycloakConfiguration注解也是对之前注解的一个复合:

@Configuration
@ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
@EnableWebSecurity
public @interface KeycloakConfiguration {
}
  1. NullAuthenticatedSessionStrategy:因为保护的是rest api,而不是基于session的所以此时需要override sessionAuthenticationStrategy;
  2. configureGlobal:这段配置来自于Keycloak的官方文档,并添加了SimpleAuthorityMapper,它的作用是一个匹配Keycloak和SpringSecurity中声明的角色大小写不同

测试

配置完成后,便可以开始测试了,使用postman发送请求,其中token来自于之前的请求:


test.png

你可能感兴趣的:(iOS Developer的全栈之路 - Keycloak(4))