之前弄好了非常非常简单的限流,现在就是登陆进来的用户,我们需要对他进行认证。这个时候就要用到我们的授权服务器了。
什么是授权服务器,就是给客户端一个身份,一个token。让我们的服务器认识他。
springCloud是提供了这样的模块的oauth2,先上依赖
<!-- 服务发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
然后实现我们自己的授权服务配置类,需要继承AuthorizationServerConfigurerAdapter
这个类的资料网上都有,我这里照搬一下
AuthorizationServerConfigurer是配置OAUth2 授权服务器的配置类接口,添加了@EnableAuthorizationServer,spring会自动注入。接口有三个方法,可以实现客户端配置、安全功能、以及各个Endpoint(端点)的相关配置
public class AuthorizationServerConfigurerAdapter implements AuthorizationServerConfigurer {
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
}
}
根据入参的名称,我们可以知道,这三个方法对应的功能
AuthorizationServerSecurityConfigurer 配置安全功能
ClientDetailsServiceConfigurer 配置我们客户端的信息
AuthorizationServerEndpointsConfigurer 配置我们端点的各个功能。
Spring Security OAuth2 是有默认的配置类的OAuth2AuthorizationServerConfiguration,当我们没有自己定义配置类时,是获取这个配置信息。这里不贴代码了
我们自己实现了配置类,重写configure(ClientDetailsServiceConfigurer clients),模拟一个客户端,
/**
* 添加第三方的客户端
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("api") //第三方客户端的名称
.secret(passwordEncoder.encode("secret")) //第三方客户端的密钥
.scopes("all") //第三方客户端的授权范围
.accessTokenValiditySeconds(3600) //获取token的有效期
.refreshTokenValiditySeconds(7*3600); //refresh_token的有效期
super.configure(clients);
}
重写configure(AuthorizationServerEndpointsConfigurer endpoints),因为我们主要的是想看看一个token的生成以及相应的认证
/**
* 配置验证管理器,UserDetailService
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService)
.tokenStore(jwtTokenStore())
.tokenEnhancer(jwtAccessTokenConverter());
//.tokenStore(redisTokenStore());
super.configure(endpoints);
}
我们来解析一下都做了什么事
// Security 设置我们的认证管理器authenticationManager,需要我们定义一个管理器对象endpoints.authenticationManager(authenticationManager)
// 重写我们的userDetailsService,后面我们可以自己模拟用户,也可以通过数据库查询我们的用户信息
.userDetailsService(userDetailsService)
//令牌的存储服务,使用JWT来为我们存储token,主要是为了把我们用户信息以及认证信息都放在token里,这样就不用一直请求我们认证服务器来获取用户的信息,减少授权服务器压力
.tokenStore(jwtTokenStore())
private TokenStore jwtTokenStore() {
JwtTokenStore jwtTokenStore = new JwtTokenStore(jwtAccessTokenConverter());
return jwtTokenStore;
}
private JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
//加载私钥
ClassPathResource classPathResource = new ClassPathResource("test.jks");
KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(classPathResource,"test".toCharArray());
jwtAccessTokenConverter.setKeyPair(keyStoreKeyFactory.getKeyPair("test","test".toCharArray()));
return jwtAccessTokenConverter;
}
额,还是先捋一捋用JWT(Json Web Token)能干啥
我们可以用公钥和私钥来加密我们的用户信息,使用Keytool来生成我们密钥对
keytool -genkeypair -alias test-keyalg RSA -keypass test
-keystore test.jks -validity 365 -storepass test
完了把我们生成好的文件放在我们的res下
//我们的token增强就是使用我们JwtAccessTokenConverter
.tokenEnhancer(jwtAccessTokenConverter());
我们的token就生成好了。
不过,我们漏掉了两个东西,先上代码
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests().anyRequest().authenticated();
//super.configure(http);
}
@Bean
public AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
@Bean
public UserDetailsService userDetailsService(){
InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
User user = new User("admin","123456", Arrays.asList(new SimpleGrantedAuthority("Role_Admin")));
inMemoryUserDetailsManager.createUser(user);
return inMemoryUserDetailsManager;
}
@Bean
public PasswordEncoder passwordEncoder(){
return NoOpPasswordEncoder.getInstance();
}
}
这个适配器帮我们定义了
AuthenticationManager,
UserDetailsService,
PasswordEncoder的策略,我们现在是模拟了一个用户出来,后续可以通过查数据库来重写UserDetailsService,认证管理器以及验证管理器先不设置了