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

相关推荐
熊大如如37 分钟前
Java 反射
java·开发语言
猿来入此小猿1 小时前
基于SSM实现的健身房系统功能实现十六
java·毕业设计·ssm·毕业源码·免费学习·猿来入此·健身平台
goTsHgo2 小时前
Spring Boot 自动装配原理详解
java·spring boot
卑微的Coder2 小时前
JMeter同步定时器 模拟多用户并发访问场景
java·jmeter·压力测试
pjx9872 小时前
微服务的“导航系统”:使用Spring Cloud Eureka实现服务注册与发现
java·spring cloud·微服务·eureka
多多*2 小时前
算法竞赛相关 Java 二分模版
java·开发语言·数据结构·数据库·sql·算法·oracle
爱喝酸奶的桃酥3 小时前
MYSQL数据库集群高可用和数据监控平台
java·数据库·mysql
唐僧洗头爱飘柔95273 小时前
【SSM-SSM整合】将Spring、SpringMVC、Mybatis三者进行整合;本文阐述了几个核心原理知识点,附带对应的源码以及描述解析
java·spring·mybatis·springmvc·动态代理·ioc容器·视图控制器
骑牛小道士3 小时前
Java基础 集合框架 Collection接口和抽象类AbstractCollection
java
alden_ygq4 小时前
当java进程内存使用超过jvm设置大小会发生什么?
java·开发语言·jvm