使用 Facebook Login OAuth for Springboot 注册用户

问题描述 投票:0回答:2

我构建了一个 Web 服务,支持通过网站上的表单本地登录和注册。现在,我想通过允许用户使用 Facebook 帐户登录/注册来补充这一点。我设法让 Facebook 的 OAuth 登录流程正常工作,但现在我试图弄清楚该流程的“注册”部分。我知道我可以添加自定义成功处理程序以将用户信息保存到数据库中。作为一个黑客示例:

@Bean
    fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
        http
            .formLogin {
                it.loginPage("/login")
                it.usernameParameter("email")
                it.passwordParameter("password")
            }
            .oauth2Login {
                it.loginPage("/login")
                it.clientRegistrationRepository(clientRegistrationRepository())
                it.successHandler { _, _, authentication ->
                    if (authentication.isAuthenticated) {
                        userRepository.save(...) // persist registration details
                    }
                }
            }

但是,我对这种方法有一些疑问,并确保我以惯用的“springboot”方式执行此操作。

第 1 部分 - 处理角色

处理角色的最佳策略是什么?对于直接通过网站注册的用户,我分配一个默认的

ROLE_USER
,但管理员也可以授予其他角色,例如:
ROLE_EDITOR
等。通过Facebook登录时,spring只是将权限
OAUTH2_USER
分配给用户。我想用用户在数据库中拥有的角色来扩充或替换它。是不是就像在 successHandler 中添加逻辑来从数据存储中获取用户信息并将数据存储中的角色添加到主体对象一样简单?

第 2 部分 - 处理用户 ID

我应该如何处理在网站上生成链接以便用户可以查看他们的个人资料?目前我这样做:

<a sec:authorize="hasRole('USER')" th:href="@{/profile/{id}(id=${#authentication.getPrincipal().id})}">View Profile</a>

但是,OAuth2User 没有 ID。与用户角色不同,它似乎也不是我可以设置的值。理想情况下,我希望避免在视图和其他地方使用自定义逻辑来确定用户是否通过 OAuth 进行身份验证。

系统信息

  • 科特林:2.3.4
  • Springboot:3.1.3
  • 春季安全:6.x
spring spring-boot oauth facebook-login spring-oauth2
2个回答
0
投票

第 1 部分:处理角色

在 UserDetails 实现中执行类似的操作来注入自定义角色

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
    return service.getRoles()
            .stream()
            .map(role -> new SimpleGrantedAuthority(role.getRole()))
            .collect(Collectors.toList());
}

第 2 部分 - 处理用户 ID

参考如何识别Google OAuth2用户?


0
投票

好吧,这就是我最终所做的。

  1. 定义一个新的 OAuth2UserService ,它实现
    OAuth2UserService
    接口
  2. 定义新用户
  3. 更新安全配置以使用新的 OAuth2UserService

OAuth用户服务

class OAuth2EmailExistsException(override val message: String): AuthenticationException(message)


@Service
class FacebookOAuth2UserService(
    private val userRepository: UserRepository,
    private val clockService: ClockService,
    private val idService: IdService,
    private val defaultOAuth2UserService: DefaultOAuth2UserService
): OAuth2UserService<OAuth2UserRequest, OAuth2User> {

    override fun loadUser(userRequest: OAuth2UserRequest): OAuth2User {
        val oauthUser = defaultOAuth2UserService.loadUser(userRequest)

        val id = oauthUser.name
        val email = oauthUser.attributes["email"] as String
        val name = oauthUser.attributes["name"] as String

        val persistedUser = userRepository.findByoAuthId(id).getOrElse {

            if (userRepository.existsByEmail(email)) {
                throw OAuth2EmailExistsException("The email associated with this Facebook account is already present in the system")
            }

            userRepository.save(User(
                id = idService.objectId(),
                firstName = name,
                lastName = "",
                password = "",
                email = email,
                status = UserStatus.ACTIVE,
                roles = setOf(SimpleGrantedAuthority(AuthRoles.USER.roleName())),
                joinDate = clockService.now().toEpochMilli(),
                timeOfPreviousNameUpdate = 0,
                oAuthId = id,
                source = RegistrationSource.FACEBOOK
            ))
        }

        return FacebookOAuth2User(persistedUser, oauthUser.attributes)
    }
}

新 OAuth2 用户

class FacebookOAuth2User(
    private val user: User,
    private val attributes: MutableMap<String, Any>
): OAuth2User, AuthUserDetails {

    val id = user.id

    override fun getUserId(): String = user.id

    // Facebook serves the name as a single entity, so we'll just store it in the
    // first name column
    override fun getName(): String = user.firstName

    override fun getAttributes(): MutableMap<String, Any> = attributes

    override fun getAuthorities(): Set<GrantedAuthority> = user.authorities

    override fun getPassword(): String = user.password

    override fun getUsername(): String = user.oAuthId!!

    override fun isAccountNonExpired() = user.isAccountNonLocked

    override fun isAccountNonLocked() = user.isAccountNonLocked

    override fun isCredentialsNonExpired() = user.isCredentialsNonExpired

    override fun isEnabled() = user.isEnabled
}

OAuth2配置

@Configuration
class OAuth2Configuration() {

    @Bean
    fun defaultOAuth2UserService(): DefaultOAuth2UserService = DefaultOAuth2UserService()
}

安全配置

@Configuration
class SecurityConfiguration(
    private val facebookOAuth2UserService: FacebookOAuth2UserService,
    private val environment: Environment
) {
    fun clientRegistrationRepository(): ClientRegistrationRepository {
        return InMemoryClientRegistrationRepository(
            CommonOAuth2Provider.FACEBOOK.getBuilder("facebook")
                .clientId(environment.getRequiredProperty("FACEBOOK_APP_KEY"))
                .clientSecret(environment.getRequiredProperty("FACEBOOK_APP_SECRET"))
                .build()
        )
    }

    @Bean
    fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
        http
            .formLogin {
                it.loginPage("/login")
                it.usernameParameter("email")
                it.passwordParameter("password")
            }
            .oauth2Login {
                it.loginPage("/login")
                it.clientRegistrationRepository(clientRegistrationRepository())
                it.userInfoEndpoint {
                    it.userService(facebookOAuth2UserService)
                }
                it.failureHandler { _, response, exception ->
                    val errorParam = when (exception) {
                        is OAuth2EmailExistsException -> "oauthEmailExists"
                        else -> "oauthError"
                    }
                    response.sendRedirect("/login?$errorParam")
                }
            }
...
© www.soinside.com 2019 - 2024. All rights reserved.