Spring Cloud进阶之路 | 二十一:授权服务(Spring Cloud Oauth2)JWT实现-TokenStore之JwtTokenStore

 

前言

前面的文章中,分别介绍了Spring Cloud进阶之路 | 十八:授权服务(Spring Cloud Oauth2)ClientDetailsService之JdbcClientDetailsService、Spring Cloud进阶之路 | 十九:授权服务(Spring Cloud Oauth2)TokenStore之JdbcTokenStore、Spring Cloud进阶之路 | 二十:授权服务(Spring Cloud Oauth2)TokenStore之RedisTokenStore等实现。然而,在现如今微服务“横行”、前后端分离的大环境下,JWT已然是业内标准。Spring Cloud Oauth2针对JWT也做了一些列封装,满足业务需求。本文即对Spring Cloud Oauth2的JWT实现做一系列说明。

 

准备工作

复用上一篇文章Spring Cloud进阶之路 | 二十:授权服务(Spring Cloud Oauth2)TokenStore之RedisTokenStore中的xmall-auth授权工程。

 

秘钥生成

既然是采用JWT实现,那么势必需要提供加密算法,分为对称加密和非对称加密算法。本文以非对称加密算法RSA为例,利用JDK 1.8自带工具keytool生成秘钥库,并导出相关秘钥文件。

生成秘钥库。

keytool -genkeypair -alias xmall-auth -keyalg RSA -keypass 123456 -keystore e:\xmall-auth.jks -storepass 123456

 

Spring Cloud进阶之路 | 二十一:授权服务(Spring Cloud Oauth2)JWT实现-TokenStore之JwtTokenStore_第1张图片

导出证书。

keytool -export -alias xmall-auth -keystore e:\xmall-auth.jks -file e:\pubkey.cer

 


Spring Cloud进阶之路 | 二十一:授权服务(Spring Cloud Oauth2)JWT实现-TokenStore之JwtTokenStore_第2张图片

打印证书。

keytool -printcert -file e:\pubkey-bin.cer

 

Spring Cloud进阶之路 | 二十一:授权服务(Spring Cloud Oauth2)JWT实现-TokenStore之JwtTokenStore_第3张图片

因为该导出方式导出的为二进制格式证书,所以也看不出来什么。所以,我们继续以BASE64格式再导出证书。

keytool -export -rfc -alias xmall-auth -keystore e:\xmall-auth.jks -file e:\pubkey-rfc.cer

Spring Cloud进阶之路 | 二十一:授权服务(Spring Cloud Oauth2)JWT实现-TokenStore之JwtTokenStore_第4张图片

打印证书。

keytool -printcert -file e:\pubkey-bin.cer

Spring Cloud进阶之路 | 二十一:授权服务(Spring Cloud Oauth2)JWT实现-TokenStore之JwtTokenStore_第5张图片

用相关软件如EditPlus、NotePad++等打开该证书,如下图所示。

Spring Cloud进阶之路 | 二十一:授权服务(Spring Cloud Oauth2)JWT实现-TokenStore之JwtTokenStore_第6张图片

注意,我们导出的是证书,而不是公钥,不要搞混。需要公钥信息,还需要对证书做进一步的处理,例如使用openssl命令导出、Java程序导出等等,本文不再赘述。

 

JWT实现改造


加载密钥对

将前面keytool工具生成的xmall-auth.jks复制到src/main/resources下,并配置加载到程序中。

private KeyPair keyPair() {
    return new KeyStoreKeyFactory(new ClassPathResource("xmall-auth.jks"), "123456".toCharArray()).getKeyPair("xmall-auth", "123456".toCharArray());
}

 

AccessTokenConverter

创建JwtAccessTokenConverter用以配置到JwtTokenStore中。

private JwtAccessTokenConverter jwtAccessTokenConverter() {
    JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
    jwtAccessTokenConverter.setKeyPair(keyPair());
    return jwtAccessTokenConverter;
}

 

JwtTokenStore

授权服务器配置TokenStore为JwtTokenStore。

@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
    endpoints.authenticationManager(authenticationManager)
        .userDetailsService(userDetailsService)
        .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
        .accessTokenConverter(jwtAccessTokenConverter())
        .tokenStore(new JwtTokenStore(jwtAccessTokenConverter()));
    ;
}

 

公钥(token key)端点

此处最容易踩坑。前面均配置完毕,发现token key节点访问404。一头雾水,明明已配置好了呀。其实,还少了一项配置,即TokenKey端点。文章避坑指南(八):Spring Cloud Oauth2 token_key端点404中已详细说明。具体配置代码如下。

@Bean
public TokenKeyEndpoint tokenKeyEndpoint() {
    return new TokenKeyEndpoint(jwtAccessTokenConverter());
}

 

最终配置

package com.luas.xmall.auth.configuration;
​
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.builders.JdbcClientDetailsServiceBuilder;
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;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.ClientRegistrationException;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.endpoint.TokenKeyEndpoint;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.*;
​
import javax.sql.DataSource;
import java.security.KeyPair;
​
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
​
    @Autowired
    private PasswordEncoder passwordEncoder;
​
    @Autowired
    private UserDetailsService userDetailsService;
​
    @Autowired
    private AuthenticationManager authenticationManager;
​
    @Autowired
    private DataSource dataSource;
​
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.
                allowFormAuthenticationForClients()
                .tokenKeyAccess("permitAll()")
                .checkTokenAccess("permitAll()")
        ;
    }
​
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService)
                .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
                .accessTokenConverter(jwtAccessTokenConverter())
                .tokenStore(new JwtTokenStore(jwtAccessTokenConverter()));
        ;
    }
​
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        // 配置方法1,只需配置DataSource即可,其它交给框架自动配置
        clients.jdbc(dataSource).passwordEncoder(passwordEncoder);
    }
​
    @Bean
    public TokenKeyEndpoint tokenKeyEndpoint() {
        return new TokenKeyEndpoint(jwtAccessTokenConverter());
    }
​
    private JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        jwtAccessTokenConverter.setKeyPair(keyPair());
        return jwtAccessTokenConverter;
    }
​
    private KeyPair keyPair() {
        return new KeyStoreKeyFactory(new ClassPathResource("xmall-auth.jks"), "123456".toCharArray()).getKeyPair("xmall-auth", "123456".toCharArray());
    }
​
​
}

至此,授权服务器改造完成。

 

验证

启动xmall-auth工程,端口7777。

访问http://localhost:7777/oauth/token,输入一众参数,诸如client_id、client_secret、grant_type等,点击Send,即出现access_token、refresh_token等信息。

Spring Cloud进阶之路 | 二十一:授权服务(Spring Cloud Oauth2)JWT实现-TokenStore之JwtTokenStore_第7张图片

已可以正常生成JWT。

访问/oauth/token_key端点,亦可以正常获取公钥。

Spring Cloud进阶之路 | 二十一:授权服务(Spring Cloud Oauth2)JWT实现-TokenStore之JwtTokenStore_第8张图片

 

 

源码

github

https://github.com/liuminglei/SpringCloudLearning/tree/master/21/

gitee

https://gitee.com/xbd521/SpringCloudLearning/tree/master/21/

 

 

 

本文系【银河架构师】原创,如需转载请在文章明显处注明作者及出处。

微信搜索【银河架构师】,发现更多精彩内容。

 

 

你可能感兴趣的:(微服务,#,Spring,Cloud)