Spring Security、OAuth2 + JWT

一、技术栈整合流程

Spring Security集成OAuth2和JWT代码示例

1. 安全配置类(SecurityConfig)
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * Spring Security核心配置类
 * 负责定义安全规则和密码加密方式
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * 配置密码加密器
     * 使用BCrypt强哈希算法加密密码
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(); // 推荐使用BCrypt加密
    }

    /**
     * 配置HTTP安全规则
     * @param http HTTP安全配置对象
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()  // 禁用CSRF保护(API场景常用)
            .authorizeRequests()
                .antMatchers("/oauth/**", "/login/**", "/logout/**").permitAll()  // 开放OAuth2相关端点
                .anyRequest().authenticated()  // 其他请求需要认证
            .and()
            .formLogin().permitAll();  // 允许表单登录
    }
}
2. JWT令牌配置类(TokenConfig)
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

/**
 * JWT令牌配置类
 * 负责JWT令牌的生成、签名和存储配置
 */
@Configuration
public class TokenConfig {
    
    // JWT签名密钥(生产环境应从安全配置读取)
    private static final String SIGNING_KEY = "your-secret-key-123456";

    /**
     * 配置JWT令牌存储策略
     * @return JWT令牌存储对象
     */
    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());  // 使用JWT存储令牌
    }

    /**
     * 配置JWT访问令牌转换器
     * 负责令牌的编码/解码和签名验证
     */
    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey(SIGNING_KEY);  // 设置JWT签名密钥
        return converter;
    }
}
3. 授权服务器配置(AuthorizationServerConfig)
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;

/**
 * OAuth2授权服务器配置
 * 负责颁发访问令牌和刷新令牌
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;  // 认证管理器
    
    @Autowired
    private PasswordEncoder passwordEncoder;  // 密码加密器
    
    @Autowired
    private TokenStore tokenStore;  // 令牌存储
    
    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;  // JWT转换器

    /**
     * 配置客户端详情服务
     * @param clients 客户端配置器
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()  // 使用内存存储(生产环境用JDBC)
            .withClient("clientapp")  // 客户端ID
            .secret(passwordEncoder.encode("123456"))  // 客户端密钥(加密存储)
            .authorizedGrantTypes("password", "refresh_token")  // 支持的授权模式
            .scopes("all")  // 授权范围
            .accessTokenValiditySeconds(3600)  // 访问令牌有效期(1小时)
            .refreshTokenValiditySeconds(86400);  // 刷新令牌有效期(24小时)
    }

    /**
     * 配置授权服务器端点
     * @param endpoints 端点配置器
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints
            .authenticationManager(authenticationManager)  // 密码模式需要
            .tokenStore(tokenStore)  // 令牌存储方式
            .accessTokenConverter(jwtAccessTokenConverter);  // 使用JWT令牌
    }

    /**
     * 配置授权服务器安全规则
     * @param security 安全配置器
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) {
        security
            .tokenKeyAccess("permitAll()")  // 公开/oauth/token_key端点
            .checkTokenAccess("isAuthenticated()")  // 认证后可访问/oauth/check_token
            .allowFormAuthenticationForClients();  // 允许客户端表单认证(解决新版本401问题)
    }
}
4. 资源服务器配置(ResourceServerConfig)
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;

/**
 * 资源服务器配置
 * 负责保护受OAuth2保护的资源
 */
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    /**
     * 配置资源访问规则
     * @param http HTTP安全配置对象
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
            .anyRequest().authenticated();  // 所有资源需要认证
    }
}

关键组件说明:

  1. 密码加密器
    BCryptPasswordEncoder使用加盐哈希算法,比明文存储更安全

  2. JWT签名机制
    通过JwtAccessTokenConverter设置签名密钥,防止令牌篡改

  3. 客户端认证方式
    allowFormAuthenticationForClients()解决新版本客户端认证问题

  4. 令牌有效期配置
    访问令牌(1小时)和刷新令牌(24小时)的合理时间设置

测试流程:

  1. 获取令牌(密码模式):

    POST /oauth/token
    Content-Type: application/x-www-form-urlencoded
    
    grant_type=password
    &username=admin
    &password=admin
    &client_id=clientapp
    &client_secret=123456
    
  2. 访问受保护资源

    GET /api/protected
    Authorization: Bearer <access_token>
    

注意事项:

  1. 密钥管理
    生产环境应将SIGNING_KEY存储在安全配置中,避免硬编码

  2. 客户端存储
    内存存储(inMemory())仅适用于测试,生产环境需改用jdbcClientDetailsService()

  3. 令牌刷新
    使用refresh_token授权类型可获取新的访问令牌:

    grant_type=refresh_token
    &refresh_token=<refresh_token>
    
  4. 用户认证
    需实现UserDetailsService加载真实用户数据(示例未展示)

思维导图

在这里插入图片描述


二、OAuth2和JWT的工作原理及核心功能相关源码解读

OAuth2与JWT工作原理及源码解析

一、OAuth2核心工作原理

OAuth2是一种授权框架,允许第三方应用在用户授权下访问受保护资源,而不暴露用户凭证。核心流程如下:

用户(资源所有者)客户端应用授权服务器资源服务器1. 请求访问资源2. 重定向到授权页3. 登录并授权4. 返回授权码5. 用授权码交换令牌6. 返回访问令牌7. 用令牌请求资源8. 返回受保护资源用户(资源所有者)客户端应用授权服务器资源服务器
源码关键接口(Spring Security OAuth2)
// 授权服务器配置入口
@Configuration
@EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
    
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
            .withClient("webapp") // 客户端ID
            .secret(passwordEncoder.encode("secret")) // 客户端密钥
            .authorizedGrantTypes("authorization_code", "refresh_token")
            .scopes("read");
    }
    
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints
            .tokenStore(tokenStore) // 令牌存储策略
            .accessTokenConverter(jwtAccessTokenConverter); // JWT转换器
    }
}

二、JWT结构解析

JWT(JSON Web Token)是无状态令牌,包含三部分:

base64UrlEncode(Header) + "." + 
base64UrlEncode(Payload) + "." + 
Signature
1. Payload结构示例
{
  "sub": "1234567890",      // 主题(用户ID)
  "name": "John Doe",       // 自定义声明
  "iat": 1516239022,        // 签发时间
  "exp": 1516239322,        // 过期时间
  "scope": "read write"     // 权限范围
}
2. 签名生成源码(JwtAccessTokenConverter)
public class JwtAccessTokenConverter implements AccessTokenConverter {
    
    private String signingKey = "secret"; // 签名密钥
    
    protected String encode(OAuth2AccessToken accessToken, 
                            OAuth2Authentication authentication) {
        
        // 1. 构建JWT声明(Claims)
        Map<String, Object> claims = new HashMap<>();
        claims.put("user_name", authentication.getName()); // 添加用户信息
        
        // 2. 生成签名(HS256算法示例)
        String encodedHeader = base64UrlEncode(header);
        String encodedClaims = base64UrlEncode(claims);
        String signature = HMACSHA256(encodedHeader + "." + encodedClaims, signingKey);
        
        return encodedHeader + "." + encodedClaims + "." + signature;
    }
}

三、OAuth2+JWT整合流程

令牌验证时序(资源服务器侧)
客户端资源服务器授权服务器请求携带JWT1. 解析JWT头部2. 验证签名(本地验证)3. 检查过期时间(exp)4. 可选:远程验证令牌(需网络)令牌有效性响应返回资源或错误客户端资源服务器授权服务器
资源服务器配置源码
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
            .antMatchers("/api/public/**").permitAll() // 公开端点
            .antMatchers("/api/private/**").authenticated(); // 需认证
    }
    
    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(jwtAccessTokenConverter()); // JWT存储策略
    }
    
    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey("secret"); // 需与授权服务器一致
        return converter;
    }
}

四、核心安全机制对比

机制OAuth2作用JWT作用
身份验证颁发访问令牌的授权流程携带用户身份的令牌格式
数据完整性依赖HTTPS传输安全通过签名防止篡改
状态管理可支持有状态会话无状态(自包含声明)
权限控制通过scope定义权限范围在claims中携带权限信息

五、最佳实践

  1. 令牌安全
    // 使用RSA非对称加密替代HS256
    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setKeyPair(keyStore.getKeyPair("mykey")); // 从密钥库加载
        return converter;
    }
    
  2. 声明扩展
    // 自定义令牌增强器
    public class CustomTokenEnhancer implements TokenEnhancer {
        @Override
        public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, 
                                         OAuth2Authentication authentication) {
            Map<String, Object> info = new HashMap<>();
            info.put("organization", authentication.getName() + "ORG");
            ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(info);
            return accessToken;
        }
    }
    

思维导图

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值