依赖包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
启动类
启动类
@SpringBootApplication
@EnableWebSecurity
public class KeycloakDemoApplication extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests(a -> a
.antMatchers("/", "/error", "/webjars/**").permitAll()
.anyRequest().authenticated()
)
.exceptionHandling(e -> e
.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))
)
.logout(l -> l
.logoutSuccessUrl("/").permitAll()
)
.csrf(c -> c
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
)
.oauth2Login();
// @formatter:on
}
public static void main(String[] args) {
SpringApplication.run(KeycloakDemoApplication.class, args);
}
}
resources/static/index.html
<div class="container unauthenticated">
With GitHub: <a href="/oauth2/authorization/keycloak">click here</a>
</div>
spring 配置
核心路径
- 执行首次跳转地址模板: /oauth2/authorization/{registrationId}
- redirectUri: 默认的模板 {baseUrl}/login/oauth2/code/{registrationId}
配置样本
spring:
security:
oauth2:
client:
registration:
keycloak:
clientId: test
clientSecret: zwd2yOiUqn0jowH2hrPYJvFsCGWnVZvL
authorizationGrantType: authorization_code
redirectUri: http://localhost:8082/login/oauth2/code/keycloak
provider:
keycloak:
authorizationUri: http://localhost:8080/realms/test/protocol/openid-connect/auth
tokenUri: http://localhost:8080/realms/test/protocol/openid-connect/token
userInfoUri: http://localhost:8080/realms/test/protocol/openid-connect/userinfo
userNameAttribute: preferred_username
代码跟踪
- DefaultOAuth2AuthorizationRequestResolver: 用于读取当前 provider 配置, 并执行跳转逻辑
- OAuth2LoginAuthenticationFilter: 用于处理callback 的地址, 执行 code 换 open id token 的操作
- OAuth2AuthorizationCodeAuthenticationProvider: 用于code 交换 access token, refresh token
- DefaultOAuth2UserService: 用于用户信息加载
- AuthorizationGrantType: 定义授权类型
遇到的问题
- 配置
userNameAttribute: preferred_username
遗漏, 会导致Missing required "user name" attribute name in UserInfoEndpoint for Client Registration