代码地址:​​https:///pshdhx/cloud-security-oauth.git​

或​​https://gitee.com/pan_shengdong/springcloud_security_oauth2.git​

分布式认证授权解决方案 1、认证授权服务

功能:接入端及登录用户的合法性进行验证,并颁发token
        AuthorizationEndpoint 服务于认证请求。默认 URL: /oauth/authorize
        TokenEndpoint 服务于访问令牌的请求。默认 URL: /oauth/token 。

资源服务

功能:保护资源,对非法请求进行拦截,对请求中的token进行鉴权操作
            OAuth2AuthenticationProcessingFilter用来对请求给出的身份令牌解析鉴权

认证流程如下

  1. 客户端请求UAA授权服务进行认证。
  2. 认证通过后由UAA颁发令牌。
  3. 客户端携带令牌Token请求资源服务。
  4. 资源服务校验令牌的合法性,合法即返回资源信息。

1.1授权服务器配置:

可以用 @EnableAuthorizationServer 注解并继承AuthorizationServerConfigurerAdapter来配置OAuth2.0授权服务器。

AuthorizationServerConfigurerAdapter要求配置以下几个类,这几个类是由Spring创建的独立的配置对象,它们 会被Spring传入AuthorizationServerConfigurer中进行配置。

1、public void configure(ClientDetailsServiceConfigurer clients) throws Exception {} 
2、public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {}
3、public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {}

配置客户端详情:

1、public void configure(ClientDetailsServiceConfigurer clients) throws Exception {} 
用来配置客户端详情;你能够把客户端详情信息写死在这里或者是通过数据库来存储调取详情信息。
// super.configure(clients);
// clients.inMemory()
// // 使用in‐memory存储
// // client_id
// .withClient("c1") (必须的)用来标识客户的Id
// //密码
// .secret(new BCryptPasswordEncoder().encode("secret")) (需要值得信任的客户端)客户端安全码,如果有的话
// .resourceIds("res1")
// // 该client允许的授权类型 authorization_code,password,refresh_token,implicit,client_credentials
// .authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "refresh_token") 此客户端可以使用的授权类型,默认为空。
// .scopes("all")//用来限制客户端的访问范围,如果为空(默认)的话,那么客户端拥有全部的访问范围
// .autoApprove(false)
// //加上验证回调地址
// .redirectUris("http://www.baidu.com");
authorities:此客户端可以使用的权限(基于Spring Security authorities)

配置令牌服务

2、public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {}
用来配置令牌(token)的访问端点和【令牌服务(token services)】。
@Bean
public AuthorizationCodeServices authorizationCodeServices() { //设置授权码模式的授权码如何 存取,暂时采用内存方式
return new InMemoryAuthorizationCodeServices();
}
AuthorizationServerTokenServicess 接口定义了一些操作使得你可以对令牌进行一些必要的管理
InMemoryTokenStore:这个版本的实现是被默认采用的,它可以完美的工作在单服务器上(即访问并发量 压力不大的情况下,并且它在失败的时候不会进行备份)
JdbcTokenStore::这是一个基于JDBC的实现版本,令牌会被保存进关系型数据库。使用这个版本的实现时, 你可以在不同的服务器之间共享令牌信息,使用这个版本的时候请注意把"spring-jdbc"这个依赖加入到你的 classpath当中。
JwtTokenStore::这个版本的全称是 JSON Web Token(JWT),它可以把令牌相关的数据进行编码(因此对 于后端服务来说,它不需要进行存储,这将是一个重大优势),但是它有一个缺点,那就是撤销一个已经授 权令牌将会非常困难,所以它通常用来处理一个生命周期较短的令牌以及撤销刷新令牌(refresh_token)。 另外一个缺点就是这个令牌占用的空间会比较大,如果你加入了比较多用户凭证信息。JwtTokenStore 不会保 @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { // clients.withClientDetails(clientDetailsService); clients.inMemory()// 使用in‐memory存储 .withClient("c1")// client_id .secret(new BCryptPasswordEncoder().encode("secret")) .resourceIds("res1") .authorizedGrantTypes("authorization_code", "password","client_credentials","implicit","refresh_token")// 该client允许的授权类型 authorization_code,password,refresh_token,implicit,client_credentials .scopes("all")// 允许的授权范围 .autoApprove(false) //加上验证回调地址 .redirectUris("http://www.baidu.com"); }
存任何数据,但是它在转换令牌值以及授权信息方面与 DefaultTokenServices 所扮演的角色是一样的。
//生成普通令牌:
@Configuration
public class TokenConfig {
@Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
}

配置安全约束

3、public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {} 
用来配置安全约束
security.tokenKeyAccess("permitAll()")
.checkTokenAccess("permitAll()")
.allowFormAuthenticationForClients();
AuthorizationServer.java
package com.pshdhx.uua.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
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;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import javax.sql.DataSource;
import java.util.Arrays;

@Configuration
@EnableAuthorizationServer
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {

@Autowired
private JwtAccessTokenConverter accessTokenConverter;

@Autowired
private TokenStore tokenStore;

@Autowired
private ClientDetailsService clientDetailsService;

@Autowired
private AuthorizationCodeServices authorizationCodeServices;

@Autowired
private AuthenticationManager authenticationManager;

/**
* 1、客户端详情配置相关
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}

@Bean
public ClientDetailsService clientDetailsService(DataSource dataSource) {
ClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
((JdbcClientDetailsService) clientDetailsService).setPasswordEncoder(passwordEncoder());
return clientDetailsService;
}

@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// super.configure(clients);
// clients.inMemory()
// // 使用in‐memory存储
// // client_id
// .withClient("c1")
// //密码
// .secret(new BCryptPasswordEncoder().encode("secret"))
// .resourceIds("res1")
// // 该client允许的授权类型 authorization_code,password,refresh_token,implicit,client_credentials
// .authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "refresh_token")
// .scopes("all")// 允许的授权范围
// .autoApprove(false)
// //加上验证回调地址
// .redirectUris("http://www.baidu.com");
clients.withClientDetails(clientDetailsService);
}






/* @Bean
public AuthorizationServerTokenServices tokenService() {
DefaultTokenServices service = new DefaultTokenServices();
service.setClientDetailsService(clientDetailsService);
service.setSupportRefreshToken(true);
service.setTokenStore(tokenStore);
service.setAccessTokenValiditySeconds(7200); // 令牌默认有效期2小时
// service.setRefreshTokenValiditySeconds(259200); // 刷新令牌默认有效期3天
return service;

}*/


//定义JWT令牌服务
// /*** 2.配置令牌服务(token services) */
@Bean
public AuthorizationServerTokenServices tokenService() {
DefaultTokenServices service = new DefaultTokenServices();
service.setClientDetailsService(clientDetailsService);
service.setSupportRefreshToken(true);//支持刷新令牌
service.setTokenStore(tokenStore); //绑定tokenStore
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(accessTokenConverter));
service.setTokenEnhancer(tokenEnhancerChain);
service.setAccessTokenValiditySeconds(7200); // 令牌默认有效期2小时
service.setRefreshTokenValiditySeconds(259200); // 刷新令牌默认有效期3天
return service;
}


@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.authenticationManager(authenticationManager)
.authorizationCodeServices(authorizationCodeServices)
.tokenServices(tokenService())
.allowedTokenEndpointRequestMethods(HttpMethod.POST);
}

/*** 3.配置令牌(token)的访问端点 */
@Bean
public AuthorizationCodeServices authorizationCodeServices() { //设置授权码模式的授权码如何 存取,暂时采用内存方式
return new InMemoryAuthorizationCodeServices();
}


/*** 4.配置令牌端点(Token Endpoint)的安全约束 */
@Override
public void configure(AuthorizationServerSecurityConfigurer security) {
security.tokenKeyAccess("permitAll()")
.checkTokenAccess("permitAll()")
.allowFormAuthenticationForClients();
}
}
TokenConfig.java
package com.pshdhx.uua.config;

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.InMemoryTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

@Configuration
public class TokenConfig {
private String SIGNING_KEY = "uaa123";

@Bean
public TokenStore tokenStore() {
//JWT令牌存储方案
return new JwtTokenStore(accessTokenConverter());
}

@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(SIGNING_KEY); //对称秘钥,资源服务器使用该秘钥来验证
return converter;
}

/* @Bean
public TokenStore tokenStore() {
//使用内存存储令牌(普通令牌)
return new InMemoryTokenStore();
}*/
}
WebSecurityConfig.java
package com.pshdhx.uua.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

//认证管理器
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
//密码编码器
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}

//安全拦截机制(最重要)
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/r/r1").hasAnyAuthority("p1")
.antMatchers("/login*").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
;

}
}

整体校验步骤

1、授权码模式:

​http://localhost:53020/uaa/oauth/authorize?client_id=c1&response_type=code&scope=all&redirect_uri=http://www.baidu.com ​

跳转至登录页;

输入账号和密码:zhangsan/123后今日此页面;

oauth2.0实现认证服务和资源服务_oauth2

授权之后重定向到此页面,同时返回客户端c1的授权码;

oauth2.0实现认证服务和资源服务_spring_02

Authentication的应用就是相当于第三方登录微信的认证,微信返回授权码;

我们再拿着授权码去请求服务方的token;

​​http://localhost:53020/uaa/oauth/token?client_id=c1&client_secret=secret&grant_type=authorization_code&code=e9wEfy&redirect_uri=http://www.baidu.com​​

oauth2.0实现认证服务和资源服务_客户端_03

2、简化token模式:

/uaa/oauth/authorize?client_id=c1&response_type=token&scope=all&redirect_uri=http://www.baidu.com

oauth2.0实现认证服务和资源服务_maven_04

3、密码模式;

​​http://localhost:53020/uaa/oauth/token?client_id=c1&client_secret=secret&grant_type=password&username=zhangsan&password=123​​

oauth2.0实现认证服务和资源服务_oauth2_05

 4、客户端模式:

​http://localhost:53020/uaa/oauth/token?client_id=c1&client_secret=secret&grant_type=client_credentials​

oauth2.0实现认证服务和资源服务_spring_06

 5、密码模式访问资源模块:

​​http://localhost:53020/uaa/oauth/token?client_id=c1&client_secret=secret&grant_type=password&username=zhangsan&password=123​​

oauth2.0实现认证服务和资源服务_maven_07

访问资源模块:

oauth2.0实现认证服务和资源服务_ide_08

 6、配置jwt令牌模式:

package com.pshdhx.config;

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.InMemoryTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

/**
 * @Author pshdhx
 * @Description 配置token类型
 * @Date 2021/8/1 0001 17:28
 */
@Configuration
public class TokenConfig {
//    @Bean
//    public TokenStore tokenStore() {
//        return new InMemoryTokenStore();
//    }
    //配置jwt令牌
    private String SIGNING_KEY = "uaa123";
    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }
    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey(SIGNING_KEY); //对称秘钥,资源服务器使用该秘钥来验证
        return converter;
    }
}


 

oauth2.0实现认证服务和资源服务_maven_09

 资源服务模块使用相同的TokenConfig.java,同时资源服务配置要解析修改的token


@Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        resources.resourceId(RESOURCE_ID)//资源 id
                .tokenStore(tokenStore)
//                .tokenServices(tokenService())//验证令牌的服务
                .stateless(true);
    }


带上jwt访问资源服务模块:

oauth2.0实现认证服务和资源服务_spring_10

获取令牌的内容

​​​http://localhost:53020/uaa/oauth/check_token?​​​

oauth2.0实现认证服务和资源服务_oauth2_11