前言
这里在对springcloud ouath2学习的过程中遇到的问题和解决办法做一个简单的总结。
开始
用Spring Cloud oAuth2的前提是必须对Spring Security有所了解,两者是相辅相成的,首先让我们对Spring Cloud oAuth2有个大概的了解:
- Spring Cloud oAuth2 主要应用于认证与授权,场景多是在不提供密码的前提下授权第三方应用访问用户的资源。( 参考:什么是oAth2)
- Spring Cloud oAuth2主要有三大块:资源所有者、授权服务提供者、客户端 。( 参考:oAuth2交互流程)
授权
Spring Cloud版本Greenwich.SR2,Spring Boot版本2.1.10.RELEASE,相关pom以及配置省略。
这里只简单实现授权服务器,首先实现Spring Security权限管控三个重要的方法:
@EnableWebSecurity
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailService userDetailService;
//配置哪些需要验证
@Override
protected void configure(HttpSecurity http) throws Exception {
//1.配置所有请求都需要经过验证
http.authorizeRequests().anyRequest().authenticated().and().csrf().disable().formLogin().permitAll();
}
//版本需要配置密码加密方式
@Bean
public PasswordEncoder passwordEncoder()
{
return new BCryptPasswordEncoder();
}
//配置验证的方式和加密方式
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailService).passwordEncoder(passwordEncoder());
}
//配置验证管理器
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
重写UserDetailsService :
//实现用户查询权限
@Service
public class UserDetailService implements UserDetailsService {
@Autowired
private UserDao userDao;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//这里的dao层是直接实现jpa
return userDao.findByUsername(username) ;
}
}
简单的spring security实现就完成了,下面实现AuthorizationServerConfigurerAdapter授权逻辑:
@Configuration
@EnableAuthorizationServer
public class AutherizationServerConfig extends AuthorizationServerConfigurerAdapter {
//注入密码验证方式,下面客户端密码也需要同样的方式
@Autowired
private PasswordEncoder passwordEncoder;
//token放在数据库中
@Autowired
@Qualifier("dataSource")
private DataSource dataSource;
//添加授权,启用密码授权的方式
@Autowired
@Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
//获取数据库中的权限等
@Autowired
private UserDetailService userDetailService;
//授权服务token安全配置
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
//不拦截所有获取token的访问
.tokenKeyAccess("permitAll()")
//验证token
.checkTokenAccess("isAuthenticated()")
.allowFormAuthenticationForClients();
}
//客户端配置
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
//客户端放在内存中(可放在数据库里面或者非关系型数据库)
.inMemory()
//客户端id
.withClient("smith")
//密码加密方式(版本必须,不然报错)
.secret(passwordEncoder.encode("smith12345"))
//这里有四种授权方式,下面专门细说
.authorizedGrantTypes("refresh_token","password")
//域
.scopes("all");
}
//授权节点服务配置
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
//存储在jdbc里面
.tokenStore(new JdbcTokenStore(dataSource))
.authenticationManager(authenticationManager)
.userDetailsService(userDetailService);
}
}
四种授权方式以及访问方式
四种方式得不同在于客户端配置(ClientDetailsServiceConfigurer)的地方,下面代码只列出对客户端配置的修改。
授权码方式authorization_code
相关配置:
clients
//客户端放在内存中(可放在数据库里面或者非关系型数据库)
.inMemory()
//客户端id
.withClient("kevin")
//密码加密方式(版本必须,不然报错)
.secret(passwordEncoder.encode("kevin12345"))
//授权码的方式
.authorizedGrantTypes("authorization_code","refresh_token")
//返回地址(后面会跟授权码)
.redirectUris("http://www.baidu.com")
//域
.scopes("all");
- 浏览器链接访问http://localhost:9002/auth/oauth/authorize?response_type=code&client_id=kevin&client_secret=kevin12345&redirect_uri=http://www.baidu.com&scope=all
- 登陆过后点击授权:
- 授权后获得验证码:
- 将刚刚获得授权码交换令牌,用post请求http://localhost:9002/auth/oauth/token?grant_type=authorization_code&client_id=kevin&client_secret=kevin12345&code=fHxxz4&redirect_uri=http://www.baidu.com
密码方式password
clients
//客户端放在内存中(可放在数据库里面或者非关系型数据库)
.inMemory()
//客户端id
.withClient("kevin")
//密码加密方式(版本必须,不然报错)
.secret(passwordEncoder.encode("kevin12345"))
//这里有四种授权方式,下面专门细说
.authorizedGrantTypes("password","refresh_token")
//域
.scopes("all");
隐式授权方式implicit
相关配置:
clients
//客户端放在内存中(可放在数据库里面或者非关系型数据库)
.inMemory()
//客户端id
.withClient("kevin")
//密码加密方式(版本必须,不然报错)
.secret(passwordEncoder.encode("kevin12345"))
//这里有四种授权方式,下面专门细说
.authorizedGrantTypes("implicit","refresh_token")
.redirectUris("http://www.baidu.com")
//域
.scopes("all");
客户端方式client_credentials
clients
//客户端放在内存中(可放在数据库里面或者非关系型数据库)
.inMemory()
//客户端id
.withClient("kevin")
//密码加密方式(版本必须,不然报错)
.secret(passwordEncoder.encode("kevin12345"))
//这里有四种授权方式,下面专门细说
.authorizedGrantTypes("client_credentials","refresh_token")
//域
.scopes("all");
四种授权模式得使用场景需要自己去领会,这里使用较少,不做赘述。