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 授权码模式的认证流程,并灵活扩展为其他授权类型(如隐式模式、密码模式)。

相关推荐
码界奇点几秒前
基于Spring Boot和Vue.js的视频点播管理系统设计与实现
java·vue.js·spring boot·后端·spring·毕业设计·源代码管理
爱吃山竹的大肚肚1 分钟前
MySQL 支持的各类索引
java·数据库·sql·mysql·spring·spring cloud
程序员水自流5 分钟前
MySQL常用内置函数详细介绍
java·数据库·mysql
高老庄小呆子6 分钟前
SpringBoot3.5.4 引入Knife4j的官方start包
spring
廋到被风吹走6 分钟前
【Spring】Spring Boot详细介绍
java·spring boot·spring
期待のcode8 分钟前
Java中的继承
java·开发语言
计算机毕设指导612 分钟前
基于微信小程序的智慧社区娱乐服务管理系统【源码文末联系】
java·spring boot·微信小程序·小程序·tomcat·maven·娱乐
期待のcode12 分钟前
Java中的super关键字
java·开发语言
禾高网络14 分钟前
互联网医院系统|禾高互联网医院|互联网医院成品
java·大数据·人工智能
nnsix15 分钟前
【C#】HttpPost请求 - Query参数 - URL编码方法
java·javascript·c#