概述
配置主要只有三个步骤:
1.resourceserver资源服务器配置,负责配置哪些url进行ouath2认证。通过继承ResourceServerConfigurerAdapter进行配置
2.AuthorizationServer授权服务器配置,负责关于token相关的配置。通过继承AuthorizationServerConfigurerAdapter进行配置
3.websecurity web安全配置,负责配置用户的查询等安全相关的基本配置
对于用户认证,spirng oath2是依赖websecurity,即对于授权服务器必须要配置websecurity
根据使用场景不同,spring oauth2分成了4种模式
- 授权码模式(authorization code)
- 简化模式(implicit)
- 密码模式(resource owner password credentials)
- 客户端模式(client credentials)
项目环境依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
配置资源服务器
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.requestMatchers().anyRequest().and()
.authorizeRequests()
.antMatchers("/getuser").permitAll()
.antMatchers("/adduser").authenticated();
}
}
/getuser不需要进行认证,/adduser需要认证
webscurity配置
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().requestMatchers().anyRequest().and().authorizeRequests()
.antMatchers("/getuser").authenticated()
.antMatchers("/adduser").authenticated()
.and()
.formLogin();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
AuthenticationManager authenticationManager = super.authenticationManagerBean();
return authenticationManager;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(getUserDetailService());
}
public UserDetailsService getUserDetailService() {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("user_1").password(new BCryptPasswordEncoder().encode("123456")).authorities("USER").build());
manager.createUser(User.withUsername("user_2").password(new BCryptPasswordEncoder().encode("123456")).authorities("USER").build());
return manager;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
/getuser、/adduser都配置为需要认证,但是不会生效,因为上面ResourceServerConfig 优先级高于webscurity。其实websecurity与ResourceServerConfig配置应该相互补充,即ResourceServerConfig没有控制的url可以用webscurity进行补充控制。
@Bean PasswordEncoder配置起到覆盖默认的DelegatingPasswordEncoder,就不会出现There is no PasswordEncoder mapped for the id "null"错误了。如果需要明文校验密码自己实现PasswordEncoder接口即可。
getUserDetailService提供了内存用户,也可以修改为查数据库。
授权服务器配置
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
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.token.store.redis.RedisTokenStore;
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
@Autowired
AuthenticationManager authenticationManager;
@Autowired
RedisConnectionFactory redisConnectionFactory;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//配置两个客户端,一个用于password认证一个用于client认证
clients.inMemory().withClient("client_1")
.authorizedGrantTypes("client_credentials", "refresh_token")
.scopes("select")
.authorities("client")
.secret(new BCryptPasswordEncoder().encode("a123a"))
.and().withClient("client_2")
.authorizedGrantTypes("password", "refresh_token")
.scopes("select")
.authorities("client")
.secret(new BCryptPasswordEncoder().encode("b123b"));
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(new RedisTokenStore(redisConnectionFactory))
.authenticationManager(authenticationManager)
.allowedTokenEndpointRequestMethods(HttpMethod.GET);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()")
.allowFormAuthenticationForClients();
}
}
注意allowFormAuthenticationForClients()这个配置,如果有这个配置获取token等走表单请求处理逻辑,不走基于basic的逻辑。
不配置allowFormAuthenticationForClients(),用浏览器访问/oauth/token会弹出basic认证的输入框,用postman访问/oauth/token会直接返回401没有权限
参考:https://blog.csdn.net/u013887008/article/details/82425805
不配置allowFormAuthenticationForClients(),走basic时,获取token过程原理参考:
https://my.oschina.net/u/2518341/blog/3020109
配置allowFormAuthenticationForClients(),获取token过程原理参考:
https://blog.csdn.net/u013815546/article/details/76977239
resourceserver和websecurity
1.资源服务器配置与websecurity配置都有http的配置
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().requestMatchers().anyRequest().and().authorizeRequests()
.antMatchers("/getuser").authenticated()
.antMatchers("/adduser").authenticated()
.and()
.formLogin();
}
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.requestMatchers().anyRequest().and()
.authorizeRequests()
.antMatchers("/getuser").permitAll()
.antMatchers("/adduser").authenticated();
}
对于/getuser上面的配置一个是permitAll() 另个一个是authenticated(),此时ResourceServerConfig 中配置的permitAll()生效。因为ResourceServerConfigurerAdapter 优先级高于WebSecurityConfigurerAdapter 。ResourceServerConfigurerAdapter 的过滤器顺序值是3,WebSecurityConfigurerAdapter的过滤器顺序值是100。
优先级分析参考:https://blog.csdn.net/yaomingyang/article/details/97481892