[TOC]
## OAuth2认证
OAuth(开放授权)是一个开放标准,允许用户授权第三方移动应用访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方移动应用或分享他们数据的所有内容,OAuth2.0是OAuth协议的延续版本,但不向后兼容OAuth 1.0即完全废止了OAuth1.0。
## 认证流程

## 主要代码
### 依赖
```
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-security</artifactId>
<version>${social.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-security</artifactId>
</dependency>
```
### 配置认证服务器
~~~
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
@Autowired
AuthenticationManager authenticationManager;
@Autowired
UserDetailsService userDetailsService;
@Qualifier("dataSource")
@Autowired
DataSource dataSource;
@Autowired
TokenStore tokenStore;
@Autowired(required = false)
private JwtAccessTokenConverter jwtAccessTokenConverter;
/**
* 定义令牌端点
* @param endpoints
* @throws Exception
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService)
.tokenStore(tokenStore)
.authorizationCodeServices(authorizationCodeServices())
.reuseRefreshTokens(false);
// jwt支持
if (jwtAccessTokenConverter != null) {
endpoints.accessTokenConverter(jwtAccessTokenConverter);
}
}
/**
* 定义令牌端点约束
* @param oauthServer
* @throws Exception
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()")
.allowFormAuthenticationForClients();
}
/**
* 客户端信息
* @param clients
* @throws Exception
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
JdbcClientDetailsServiceBuilder builder = clients.jdbc(dataSource);
}
/**
* 使用jdbc数据源的授权码
* @return
*/
@Bean
protected AuthorizationCodeServices authorizationCodeServices() {
return new JdbcAuthorizationCodeServices(dataSource);
}
}
~~~
### 配置WebSecurityConfigurer
~~~
@Configuration
@EnableWebSecurity
@EnableConfigurationProperties(PermitUrlProperties.class)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private PermitUrlProperties permitUrlProperties;
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
SecurityProperties securityProperties = new SecurityProperties();
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.formLogin().loginPage(FromLoginConstant.LOGIN_PAGE).loginProcessingUrl(FromLoginConstant.LOGIN_PROCESSING_URL)
.and()
.authorizeRequests().antMatchers(
"/oauth/**",
FromLoginConstant.LOGIN_PROCESSING_URL,
FromLoginConstant.LOGIN_PAGE,
securityProperties.getOauthLogin().getOauthLogin(),
securityProperties.getOauthLogin().getOauthGrant()).permitAll().anyRequest().authenticated().and()
.csrf().disable();
// @formatter:on
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(permitUrlProperties.getIgnored());
}
}
~~~
### 配置tokenStore
~~~
@Slf4j
@Configuration
public class TokenStoreConfig {
@Resource
private DataSource dataSource;
@Resource
RedisProperties redisProperties;
@Autowired(required = false)
private RedisTemplate<String, Object> redisTemplate;
@Bean
@ConditionalOnProperty(prefix = "jfun", name = "tokenStoreType", havingValue = "jdbc")
public JdbcTokenStore jdbcTokenStore() {
log.info("-------------> 使用jdbc token");
return new JdbcTokenStore(dataSource);
}
@Bean
@ConditionalOnProperty(prefix = "jfun", name = "tokenStoreType", havingValue = "redis")
public RedisTokenStore redisStore(RedisConnectionFactory redisConnectionFactory) {
//redis 集群
log.info("-------------> 使用redis token");
RedisTokenStore redisTemplateStore = new RedisTokenStore(redisConnectionFactory);
return redisTemplateStore;
}
@Configuration
@ConditionalOnProperty(prefix = "jfun", name = "type", havingValue = "tokenStoreType")
public static class JwtTokenConfig {
@Bean
public JwtTokenStore jwtTokenStore() {
log.info("-------------> 使用jwt token");
return new JwtTokenStore(jwtAccessTokenConverter());
}
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
// keytool -genkeypair -alias jfuncloud -keyalg RSA -keypass jfuncloud -keystore jfuncloud.jks -storepass jfuncloud
// keytool -list -rfc --keystore jfuncloud.jks | openssl x509 -inform pem -pubkey
//使用非对称加密
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
KeyStoreKeyFactory keyStoreKeyFactory =
new KeyStoreKeyFactory(new ClassPathResource("jfuncloud.jks"), "jfuncloud".toCharArray());
converter.setKeyPair(keyStoreKeyFactory.getKeyPair("jfuncloud"));
converter.setAccessTokenConverter(new ResJwtAccessTokenConverter());
return converter;
}
}
}
~~~