《Spring Microservices in Action》
《Spring Cloud Alibaba 微服务原理与实战》
《B站 尚硅谷 SpringCloud 框架开发教程 周阳》
OAuth2 是一个基于令牌的安全验证和授权框架。他允许用户使用第三方验证服务进行验证。 如果用户成功进行了验证, 则会出示一个令牌,该令牌必须与每个请求一起发送。然后,验证服务可以对令牌进行确认;
security.oauth2.resource.userInfoUri
中定义的回调 URL 告诉客户端与 OAuth2 认证服务器交互,查看令牌是否有效;
<dependency>
<groupid>org.springframework.cloudgroupid>
<artifactid>spring-cloud-securityartifactid>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-oauth2artifactId>
dependency>
在 controller 包下;
/auth/user
端点,当受保护的服务调用 /auth/user
时,将会确认 OAuth2 访问令牌,并检索发文手背欧虎服务所分配的角色;/**
* 用户信息校验
* 由受保护服务调用,确认 OAuth2 访问令牌,并检索访问受保护服务的用户所分配的角色
* @param OAuth2Authentication 信息
* @return 用户信息
*/
@RequestMapping(value = { "/user" }, produces = "application/json")
public Map<String, Object> user(OAuth2Authentication user) {
Map<String, Object> userInfo = new HashMap<>();
userInfo.put("user", user.getUserAuthentication().getPrincipal());
userInfo.put("authorities", AuthorityUtils.authorityListToSet(user.getUserAuthentication().getAuthorities()));
return userInfo;
}
在 config 包下;
ClientDetailsServiceConfigurer
支持两种类型的储存:内存存储和JDBC存储,如下分点所示:@Configuration
//继承 AuthorizationServerConfigurerAdapter 类
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
@Autowired
private DataSource dataSource;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserDetailsService userDetailsService;
@Override
//定义哪些客户端将注册到服务
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//JDBC存储:
JdbcClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
clientDetailsService.setSelectClientDetailsSql(SecurityConstants.DEFAULT_SELECT_STATEMENT); //设置我们的自定义的sql查找语句
clientDetailsService.setFindClientDetailsSql(SecurityConstants.DEFAULT_FIND_STATEMENT); //设置我们的自定义的sql查找语句
clients.withClientDetails(clientDetailsService); //从 jdbc 查出数据来存储
}
@Override
//使用 Spring 提供的默认验证管理器和用户详细信息服务
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
}
public interface SecurityConstants {
/**
* sys_oauth_client_details 表的字段,不包括client_id、client_secret
*/
String CLIENT_FIELDS = "client_id, client_secret, resource_ids, scope, "
+ "authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, "
+ "refresh_token_validity, additional_information, autoapprove";
/**
*JdbcClientDetailsService 查询语句
*/
String BASE_FIND_STATEMENT = "select " + CLIENT_FIELDS + " from sys_oauth_client_details";
/**
* 默认的查询语句
*/
String DEFAULT_FIND_STATEMENT = BASE_FIND_STATEMENT + " order by client_id";
/**
* 按条件client_id 查询
*/
String DEFAULT_SELECT_STATEMENT = BASE_FIND_STATEMENT + " where client_id = ?";
}
@Configuration
//继承 AuthorizationServerConfigurerAdapter 类
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserDetailsService userDetailsService;
@Override
//定义哪些客户端将注册到服务
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("eagleeye") //名称
.secret("thisissecret") //密钥
.authorizedGrantTypes("refresh_token", "password", "client_credentials") //授权类型列表
.scopes("webclient", "mobileclient"); //获取访问令牌时可以操作的范围
}
@Override
//使用 Spring 提供的默认验证管理器和用户详细信息服务
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
}
在 config 包下:
@Configuration
@EnableWebSecurity
//扩展核心 Spring Security 的 WebSecurityConfigurerAdapter
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
//用来处理验证
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
//处理返回用户信息
@Override
@Bean
public UserDetailsService userDetailsServiceBean() throws Exception {
return super.userDetailsServiceBean();
}
//configure() 方法定义用户、密码与角色
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("john.carnell").password("password1").roles("USER")
.and()
.withUser("william.woodward").password("password2").roles("USER", "ADMIN");
}
}
john.carnell
用户拥有 USER 用户;william.woodward
拥有 ADMIN 用户;http://localhost:8901/auth/oauth/token
;
<dependency>
<groupid>org.springframework.cloudgroupid>
<artifactid>spring-cloud-securityartifactid>
dependency>
<dependency>
<groupId>org.springframework.security.oauthgroupId>
<artifactId>spring-security-oauth2artifactId>
dependency>
security:
oauth2:
resource:
userInfoUri: http://localhost:8901/auth/user
/auth/user
端点,向 OAuth2 服务器检查访问令牌是否生效;security.oauth2.resource.userInfoUri
中定义的回调 URL 来查看令牌是否有效;@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker //断路器
@EnableResourceServer //表示受保护资源
public class Application {
//注入一个过滤器,会拦截对服务的所有传入调用
@Bean
public Filter userContextFilter() {
UserContextFilter userContextFilter = new UserContextFilter();
return userContextFilter;
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
在 config 包或 security 包下;
configure()
方法;//必须使用该注解,且需要扩展 ResourceServerConfigurerAdapter 类
@Configuration
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
//访问规则在 configure() 方法中定义,并且通过传入方法的 HttpSecurity 对象配置
@Override
public void configure(HttpSecurity http) throws Exception{
http.authorizeRequests().anyRequest().authenticated();
}
}
anyRequest().authenticated()
表示需要由已通过验证的用户访问;@Configuration
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception{
http
.authorizeRequests()
.antMatchers(HttpMethod.DELETE, "/v1/xxxservices/**") //运行部开发人员限制对受保护的 URL 和 HTTP DELETE 动词的调用
.hasRole("ADMIN") //允许访问的角色列表
.anyRequest()
.authenticated();
}
}
anyRequest().authenticated()
表示仍需要由已通过验证的用户访问;在 Zuul 的 application.yml 的配置文件里;
因为在整个验证流程中,我们需要将 HTTP 首部 Authorization 传递上下游进行权限认证;
但在默认情况下,Zuul 不会将敏感的 HTTP 首部(如 Cookie、Set-Cokkie 和 Authorization)转发到下游服务;
需要配置 Zuul 的黑名单放行 Authorization;
zuul:
sensitiveHeaders: Cookie , Set-Cookie
上述配置表示拦截 Cookie , Set-Cookie 传递下游,而 Authorization 会放行;
可以在主程序类上,也可以在主程序所在包及其子包里创建类;
使该类可以被自动装配到调用另一个受 OAuth2 保护的服务;
@Bean
public OAuth2RestTemplate oauth2RestTemplate(OAuth2ClientContext oauth2ClientContext, OAuth2ProtectedResourceDetails details) {
return new OAuth2RestTemplate(details, oauth2ClientContext);
}
@Component
public class OrganizationRestTemplateClient {
//OAuth2RestTemplate 是标准的 RestTemplate 的增强式替代品,可处理 OAuth2 访问令牌
@Autowired
OAuth2RestTemplate restTemplate;
public Organization getOrganization(String organizationId){
//调用组织服务的方式与标准的 RestTemplate 完全相同
ResponseEntity<Organization> restExchange =
restTemplate.exchange(
"http://zuulserver:5555/api/organization/v1/organizations/{organizationId}",
HttpMethod.GET,
null, Organization.class, organizationId);
return restExchange.getBody();
}
}