oauth2: OAuth 2.0关注客户端开发者的简易性。要么通过组织在资源拥有者和HTTP服务商之间的被批准的交互动作代表用户,要么允许第三方应用代表用户获得访问的权限。
至于oauth2具体的理解可以参考阮一峰的关于oauth的博客,那篇博客将的很同事易懂,我这边就不细讲这个了,
在oauth2中有几种授权模式:
1、授权码模式
2、密码模式
3、客户端模式
4、简易模式
5、刷新模式
你们可以对着阮一峰的博客去看一下这几种模式,在这里我们就不说了。我们直接进入到今天的主题oauth2。
在oauth中我们通常将他分为认证中心和资源中心。因为我们是有oauth的目的其实就是想通过token去访问资源,所以在我们的oauth也就是基于这两块来弄的
认证中心:
在oauth中这个是非常重要的,你可以理解为这边会生成我们的token以及管理我们的token,这里同时也是进行用户的检验的认证。我们先看一下如何配置认证中心,然后在基于认证中心里面的代码来讲解、
首先我们要建一个类并集成AuthorizationServerConfigurerAdapter,然后在类上面添加
@Configuration
@EnableAuthorizationServer(这个代表的我们这个类是认证中心)
如下代码所示
@Configuration
@EnableAuthorizationServer
public class OauthConfig extends AuthorizationServerConfigurerAdapter{
@Autowired
AuthenticationManager authenticationManager;
@Autowired
RedisConnectionFactory redisConnectionFactory;
@Autowired
UserDetailsService userDetailsService;
@Autowired
ClientDetailsService clientDetails;
@Autowired
private DataSource dataSource;
@Bean // 声明TokenStore实现
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetails);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService)
// 2018-4-3 增加配置,允许 GET、POST 请求获取 token,即访问端点:oauth/token
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
endpoints.tokenServices(tokenServices());
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
//允许表单认证
oauthServer.allowFormAuthenticationForClients();
}
@Bean
@Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setSupportRefreshToken(true);
tokenServices.setTokenStore(tokenStore());
tokenServices.setAccessTokenValiditySeconds(7200);
return tokenServices;
}
}
这上面的代码就是简单的基于认证中心的代码,我们就一一讲解一下他每个方法里面的代码的含义
首先 configure(ClientDetailsServiceConfigurer clients)
这个方法主要是用于校验注册的第三方客户端的信息,我们这边使用的是数据库去存储第三方客户端的信息,如果使用内存存的话可以使用如下的代码去存储
// //配置两个客户端,一个用于password认证一个用于client认证
clients.inMemory().withClient("client_1")
.resourceIds("zjoauth")//资源名称
.authorizedGrantTypes("client_credentials", "refresh_token")//支持哪种模式,多种使用逗号分隔
.scopes("select")//具有什么权限,读写等
.authorities("client")//权限
.secret("123456")//密码
.and().withClient("client_2")
.resourceIds("zjoauth")
.authorizedGrantTypes("password", "refresh_token")
.scopes("select")
.authorities("client")
.secret("123456");
其实在内存存第三方的信息的这些字段,我上面也一一介绍了这几个字段的含义,而数据库存储也是基于上面的这些字段,这个用户信息可以这样理解,比如你想使用qq的第三方登录,qq的第三方登录也是基于oauth2来实现的,你必须先去qq上面建一个开发者账号,而我们这里的用户可以理解你注册的开发者账号。
configure(AuthorizationServerEndpointsConfigurer endpoints)方法,这个方法主要的作用用于控制token的端点等信息。
我们在这里生成token以及token的信息检验,可能好多人会问这里怎么有个userDetailsService这个啊,之前我们的security也有啊,如果你看过阮一峰的博客就会知道这里的作用是为了密码模式而是用的。
然后这个方法里面既然是实现token的端点的。那么就需要配置token的内容了
而 endpoints.tokenServices(tokenServices());就是配置关于token的
而这个 tokenServices()就是我们的配置
@Bean
@Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setSupportRefreshToken(true);//支持刷新模式
tokenServices.setTokenStore(tokenStore());//是用数据库存储token,这个框架默认已经实现了一个,我们没有特别的要求可以直接使用
tokenServices.setAccessTokenValiditySeconds(7200);//设置token的时长
return tokenServices;
}
configure(AuthorizationServerSecurityConfigurer oauthServer)这个方法我一直没有搞懂 ,好像默认就是
//允许表单认证
oauthServer.allowFormAuthenticationForClients();
既然我们这篇是基于数据库来存储token等信息的,那么就需要有一些数据库的表,这个大家可以去网上找,大致会生成5张表来管理这些数据。我这边就不说了。
然后我们这边在讲下客户端信息的实现类,在我们注册开发者的那边,我们需要实现这个开发者的信息,比如他能访问什么,以及读写权限,这些在我们获取token之前都必须传给我们的认证服务器的,然后取认证的。
其实实现这个和之前security说的很像,也是实现ClientDetailsService这个接口,然后也是重写这个loadClientByClientId方法,
@Override
public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException {
// TODO Auto-generated method stub
ClientDetail client =dao.getById(clientId);
ClientDetails clientD = new SampleClientDetails(client);
return clientD;
}
我们在看看我们的SampleClientDetails这个类,这个类主要是实现了我们数据库表的一些信息
public class SampleClientDetails implements ClientDetails {
ClientDetail clientDetail;
public SampleClientDetails(ClientDetail clientDetail) {
this.clientDetail = clientDetail;
}
@Override
public String getClientId() {
// TODO Auto-generated method stub
return clientDetail.getClient_id();
}
@Override
public Set<String> getResourceIds() {
// TODO Auto-generated method stub
String resids=clientDetail.getResource_ids();
if(resids!=null){
Set<String> s=new HashSet<>();
String[] re=resids.split(",");
for(int i=0;i<re.length;i++){
s.add(re[i]);
}
return s;
// return Stream.of(resids.split(",")).collect(Collectors.toSet());
}
return null;
}
@Override
public boolean isSecretRequired() {
// TODO Auto-generated method stub
return true;
}
@Override
public String getClientSecret() {
// TODO Auto-generated method stub
return clientDetail.getClient_secret();
}
@Override
public boolean isScoped() {
// TODO Auto-generated method stub
return true;
}
@Override
public Set<String> getScope() {
// TODO Auto-generated method stub
String scope=clientDetail.getScope();
if(scope!=null){
Set<String> s=new HashSet<>();
String[] re=scope.split(",");
for(int i=0;i<re.length;i++){
s.add(re[i]);
}
return s;
}
return null;
}
@Override
public Set<String> getAuthorizedGrantTypes() {
// TODO Auto-generated method stub
String grantype=clientDetail.getAuthorized_grant_types();
if(grantype!=null){
// return Stream.of(grantype.split(",")).collect(Collectors.toSet());
Set<String> s=new HashSet<>();
String[] re=grantype.split(",");
for(int i=0;i<re.length;i++){
s.add(re[i]);
}
return s;
}
return null;
}
@Override
public Set<String> getRegisteredRedirectUri() {
// TODO Auto-generated method stub
return null;
}
@Override
public Collection<GrantedAuthority> getAuthorities() {
// TODO Auto-generated method stub
List<GrantedAuthority> auths = new ArrayList<GrantedAuthority>();
auths.add(new GrantedAuthority() {
private static final long serialVersionUID = 1L;
@Override
public String getAuthority() {
return "USER";
}
});
return auths;
}
@Override
public Integer getAccessTokenValiditySeconds() {
// TODO Auto-generated method stub
return clientDetail.getAccess_token_validity();
}
@Override
public Integer getRefreshTokenValiditySeconds() {
// TODO Auto-generated method stub
return clientDetail.getRefresh_token_validity();
}
@Override
public boolean isAutoApprove(String scope) {
// TODO Auto-generated method stub
if(scope!=null && scope.equals(clientDetail.getAutoapprove())){
return true;
}
return false;
}
@Override
public Map<String, Object> getAdditionalInformation() {
// TODO Auto-generated method stub
return null;
}
}
到此为止我们的认证中心也就讲解完毕了。