spring security +kotlin 实现oauth2.0 认证

基于OAuth 2.0的认证功能实现(Kotlin + Spring Security)

以下是使用 AbstractAuthenticationProcessingFilterAuthenticationProviderAbstractAuthenticationTokenAuthenticationSuccessHandler 实现 OAuth 2.0 认证的完整代码设计。


1. 自定义认证令牌:OAuth2AuthenticationToken

kotlin 复制代码
import org.springframework.security.authentication.AbstractAuthenticationToken
import org.springframework.security.core.GrantedAuthority

class OAuth2AuthenticationToken(
    private val code: String,          // 授权码(Credentials)
    private val clientId: String,      // 客户端ID(Principal)
    authorities: List<GrantedAuthority> = emptyList()
) : AbstractAuthenticationToken(authorities) {

    init {
        isAuthenticated = false       // 初始状态未认证
    }

    override fun getPrincipal(): Any = clientId
    override fun getCredentials(): Any = code

    // 认证成功后调用此方法设置权限
    fun setAuthenticated(authorized: Boolean, authorities: List<GrantedAuthority>) {
        require(authorized) { "Cannot set to unauthenticated" }
        super.setAuthenticated(true)
        super.setDetails(authorities)
    }
}

2. 自定义认证过滤器:OAuth2AuthenticationFilter

kotlin 复制代码
import org.springframework.security.core.Authentication
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse

class OAuth2AuthenticationFilter(defaultFilterProcessesUrl: String) :
    AbstractAuthenticationProcessingFilter(defaultFilterProcessesUrl) {

    override fun attemptAuthentication(
        request: HttpServletRequest,
        response: HttpServletResponse
    ): Authentication {
        // 从请求中提取 OAuth2 参数
        val code = request.getParameter("code") ?: throw MissingCodeException()
        val clientId = request.getParameter("client_id") ?: throw MissingClientIdException()

        // 创建未认证的 Token
        val authRequest = OAuth2AuthenticationToken(code, clientId)
        
        // 提交给 AuthenticationManager 进行认证
        return authenticationManager.authenticate(authRequest)
    }
}

3. 自定义认证提供器:OAuth2AuthenticationProvider

kotlin 复制代码
import org.springframework.security.authentication.AuthenticationProvider
import org.springframework.security.core.Authentication
import org.springframework.security.core.authority.SimpleGrantedAuthority

class OAuth2AuthenticationProvider(
    private val oAuth2Service: OAuth2Service // 自定义的 OAuth2 服务
) : AuthenticationProvider {

    override fun supports(authentication: Class<*>): Boolean {
        return OAuth2AuthenticationToken::class.java.isAssignableFrom(authentication)
    }

    override fun authenticate(authentication: Authentication): Authentication {
        val token = authentication as OAuth2AuthenticationToken
        val code = token.credentials as String
        val clientId = token.principal as String

        // 调用 OAuth2 服务验证授权码并获取用户信息
        val userInfo = oAuth2Service.exchangeCodeForUserInfo(code, clientId)
        
        // 构建认证成功的 Token
        return OAuth2AuthenticationToken(
            code = code,
            clientId = clientId,
            authorities = userInfo.roles.map { SimpleGrantedAuthority("ROLE_$it") }
        ).apply {
            setAuthenticated(true, authorities)
            details = userInfo // 附加用户详细信息
        }
    }
}

4. 自定义成功处理器:OAuth2AuthenticationSuccessHandler

kotlin 复制代码
import com.fasterxml.jackson.databind.ObjectMapper
import org.springframework.security.core.Authentication
import org.springframework.security.web.authentication.AuthenticationSuccessHandler
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse

class OAuth2AuthenticationSuccessHandler(
    private val objectMapper: ObjectMapper
) : AuthenticationSuccessHandler {

    override fun onAuthenticationSuccess(
        request: HttpServletRequest,
        response: HttpServletResponse,
        authentication: Authentication
    ) {
        response.contentType = "application/json"
        response.characterEncoding = "UTF-8"
        
        // 生成响应数据(如 JWT 或用户信息)
        val userInfo = authentication.details as UserInfo
        val accessToken = generateJwtToken(userInfo)
        
        val result = mapOf(
            "access_token" to accessToken,
            "user_id" to userInfo.id,
            "roles" to userInfo.roles
        )
        
        response.writer.write(objectMapper.writeValueAsString(result))
    }

    private fun generateJwtToken(userInfo: UserInfo): String {
        // 实现 JWT 生成逻辑(示例使用 jjwt)
        return Jwts.builder()
            .setSubject(userInfo.id)
            .claim("roles", userInfo.roles)
            .signWith(SignatureAlgorithm.HS256, "your-secret-key")
            .compact()
    }
}

5. 配置 Spring Security

kotlin 复制代码
@Configuration
@EnableWebSecurity
class SecurityConfig(
    private val oAuth2Service: OAuth2Service,
    private val objectMapper: ObjectMapper
) : WebSecurityConfigurerAdapter() {

    // 注册认证过滤器
    @Bean
    fun oauth2Filter(): OAuth2AuthenticationFilter {
        val filter = OAuth2AuthenticationFilter("/oauth2/login")
        filter.setAuthenticationSuccessHandler(successHandler())
        filter.setAuthenticationManager(authenticationManagerBean())
        return filter
    }

    // 注册认证提供器
    @Bean
    override fun authenticationManagerBean(): AuthenticationManager {
        return ProviderManager(listOf(oAuth2AuthenticationProvider()))
    }

    @Bean
    fun oAuth2AuthenticationProvider(): OAuth2AuthenticationProvider {
        return OAuth2AuthenticationProvider(oAuth2Service)
    }

    @Bean
    fun successHandler(): OAuth2AuthenticationSuccessHandler {
        return OAuth2AuthenticationSuccessHandler(objectMapper)
    }

    override fun configure(http: HttpSecurity) {
        http
            .csrf().disable()
            .authorizeRequests()
            .antMatchers("/oauth2/login").permitAll()
            .anyRequest().authenticated()
            .and()
            .addFilterBefore(oauth2Filter(), UsernamePasswordAuthenticationFilter::class.java)
    }
}

6. 辅助类定义

kotlin 复制代码
// OAuth2 服务接口
interface OAuth2Service {
    fun exchangeCodeForUserInfo(code: String, clientId: String): UserInfo
}

// 用户信息数据类
data class UserInfo(
    val id: String,
    val name: String,
    val roles: List<String>
)

// 自定义异常
class MissingCodeException : AuthenticationException("Missing 'code' parameter")
class MissingClientIdException : AuthenticationException("Missing 'client_id' parameter")

核心流程说明

  1. 客户端请求

    发送请求到 /oauth2/login?code=xxx&client_id=client1,携带授权码和客户端 ID。

  2. 过滤器拦截
    OAuth2AuthenticationFilter 提取参数并创建 OAuth2AuthenticationToken

  3. 认证提供器处理
    OAuth2AuthenticationProvider 调用 OAuth2 服务验证授权码,返回用户信息并构建认证成功的 Token。

  4. 成功响应
    OAuth2AuthenticationSuccessHandler 生成 JWT 令牌并返回 JSON 响应。


安全增强建议

  1. HTTPS 强制使用

    kotlin 复制代码
    http.requiresChannel().anyRequest().requiresSecure()
  2. 令牌有效期管理

    kotlin 复制代码
    Jwts.builder()
        .setExpiration(Date(System.currentTimeMillis() + 3600 * 1000))
  3. 密钥安全存储

    使用环境变量或配置服务器管理密钥:

    kotlin 复制代码
    @Value("\${jwt.secret}")
    private lateinit var jwtSecret: String

通过以上设计,可实现基于 OAuth 2.0 授权码模式的认证流程,并灵活扩展为其他授权类型(如隐式模式、密码模式)。

相关推荐
极客先躯6 分钟前
高级java每日一道面试题-2025年4月13日-微服务篇[Nacos篇]-Nacos如何处理网络分区情况下的服务可用性问题?
java·服务器·网络·微服务·nacos·高级面试
pwzs14 分钟前
Spring MVC 执行流程全解析:从请求到响应的七步走
java·后端·spring·spring mvc
我该如何取个名字1 小时前
Mac配置Java的环境变量
java·开发语言·macos
kkkkatoq1 小时前
Java中的锁
java·开发语言
界面开发小八哥1 小时前
「Java EE开发指南」用MyEclipse开发EJB 3无状态会话Bean(二)
java·ide·java-ee·eclipse·myeclipse
soulermax1 小时前
数字ic后端设计从入门到精通2(含fusion compiler, tcl教学)
java·linux·服务器
我的代码永没有bug2 小时前
day1-小白学习JAVA---JDK安装和环境变量配置(mac版)
java·学习·macos
王有品2 小时前
Spring MVC 一个简单的多文件上传
java·spring·mvc
Johnny Lnex2 小时前
JVM之经典垃圾回收器
java