我正在开发一个API服务,它可以完成以下任务:
1.允许用户通过Google登录。
1.根据检索到的信息在数据库中创建用户。
1.为用户提供一个用于身份验证的JWT令牌,以便对所述用户的请求进行唯一标识。
1.允许用户能够使用获取的令牌对我的服务执行API请求。
spring:
security:
oauth2:
client:
registration:
google:
client-id: ${GOOGLE_CLIENT_ID}
client-secret: ${GOOGLE_CLIENT_SECRET}
我不确定我该怎么做,我到底需要什么。到目前为止,我有以下内容
主应用程序类:
@SpringBootApplication
@EnableWebSecurity
@Configuration
class ApiServiceApplication {
@Bean
fun filterChain(http: HttpSecurity): SecurityFilterChain {
http.authorizeHttpRequests {
it.antMatchers("/", "/login", "/error", "/webjars/**").permitAll().anyRequest().authenticated()
}
.logout {
it.logoutSuccessUrl("/").permitAll()
}
.exceptionHandling {
it.authenticationEntryPoint(HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))
}
.oauth2Login { oauth2Login ->
oauth2Login.loginPage("/login")
oauth2Login.defaultSuccessUrl("/user", true)
}
.oauth2Client { oauth2Client -> }
.csrf {
it.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
}
return http.build()
}
}
fun main(args: Array<String>) {
runApplication<ApiServiceApplication>(*args)
}
用于将用户保存到DB的用户服务类
@RestController
class UserService : OidcUserService() {
@Autowired
lateinit var userRepository: UserRepository
@Autowired
lateinit var loginRepository: LoginRepository
private val oauth2UserService = DefaultOAuth2UserService()
@GetMapping("/login")
fun authenticate(): RedirectView {
return RedirectView("/oauth2/authorization/google")
}
override fun loadUser(userRequest: OidcUserRequest?): OidcUser {
val loadedUser = oauth2UserService.loadUser(userRequest)
val username = loadedUser.attributes["email"] as String
var user = userRepository.findByUsername(username)
if (user == null) {
user = OauthUser()
user.username = username
}
loadedUser.attributes.forEach { loadedAttribute ->
val userAttribute = user.oauthAttributes.find { loadedAttribute.key == it.attributeKey && it.active }
val newAttribute = OauthAttribute(loadedAttribute.key, loadedAttribute.value?.toString())
if(userAttribute == null){
user.oauthAttributes.add(newAttribute)
}
else if(userAttribute.attributeValue != loadedAttribute.value?.toString()){
userAttribute.active = false
user.oauthAttributes.add(newAttribute)
}
}
user.oauthAuthorities = loadedUser.authorities.map { OauthAuthority(it.authority) }.toMutableList()
user.oauthToken = OauthToken(
userRequest?.accessToken?.tokenValue!!,
Date.from(userRequest.accessToken.issuedAt),
Date.from(userRequest.accessToken.expiresAt)
)
userRepository.save(user)
val login = Login(user)
loginRepository.save(login)
return user
}
}
我没有提供数据类和相应的存储库,因为上面的操作很好--在访问/login端点时,用户被重定向到Google,在那里,经过身份验证后,用户连同相应的信息沿着被保存在数据库中。
我的主要问题是我不确定如何验证每个请求。我尝试在Postman中提供一个验证载体,它与从Google通过loadUser
方法获得的验证载体相同,但我得到了401个未授权代码。当我通过浏览器访问服务器并进行验证时,我可以很好地访问所有端点。但我猜只有我的会话是经过认证的。
2条答案
按热度按时间wj8zmpe11#
您正在尝试将资源服务器(REST API服务资源)配置为UI客户端(应用程序消耗资源)。这将无法工作。
您不应该在resource-server上实现oauth2登录和注销,这是UI客户端问题,应该从您的Java配置中删除。一个例外是,如果您的应用程序还提供带有Thymeleaf、JSF或其他服务器端呈现UI的UI,在这种情况下,您应该创建第二个“客户端”安全过滤器链bean,并将登录和注销移动到那里,如此处所述:将KeycloakSpring适配器与 Spring Boot 一起使用3)。
除非您处于上述“异常”(UI客户端)中,或者使用由spring-boot自动配置的REST客户端(
WebClient
、@FeignClient
、RestTemplate
)消耗其他资源服务器的资源,否则请从yaml文件中删除所有spring.security.oauth2.client
属性,并从依赖项中删除spring-boot-starter-oauth2-client
。配置资源服务器的详细信息请参见上面链接的答案(适用于任何OIDC授权服务器,而不仅仅是Keycloak)或this repo of mine的教程。
jk9hmnmh2#
我已经通过以下方式实现了我的目标:
向
spring.security.oauth2
配置添加资源服务器定义:添加
OAuth2ResourceServerConfigurer
并通过.oauth2ResourceServer().jwt()
指定默认的JwtConfigurer
,并指定要由JWT保护的路径的授权匹配。由于ch4mp的注解,我还拆分了过滤器链,以便只有/api
端点通过JWT保护:现在,在Map到路径的方法中,我可以执行一些更具体的身份验证逻辑: