《Spring Microservices in Action》
《Spring Cloud Alibaba 微服务原理与实战》
《B站 尚硅谷 SpringCloud 框架开发教程 周阳》
OAuth2 是一个基于令牌的安全验证和授权框架。他允许用户使用第三方验证服务进行验证。 如果用户成功进行了验证, 则会出示一个令牌,该令牌必须与每个请求一起发送。然后,验证服务可以对令牌进行确认;

security.oauth2.resource.userInfoUri 中定义的回调 URL 告诉客户端与 OAuth2 认证服务器交互,查看令牌是否有效;<!--security 通用安全库--><dependency> <groupid>org.springframework.cloud</groupid> <artifactid>spring-cloud-security</artifactid> </dependency> <!--oauth2.0--><dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-oauth2</artifactId></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 的 WebSecurityConfigurerAdapterpublic 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;<!--security 通用安全库--><dependency> <groupid>org.springframework.cloud</groupid> <artifactid>spring-cloud-security</artifactid> </dependency> <!--oauth2.0--><dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth2</artifactId></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 类@Configurationpublic class ResourceServerConfiguration extends ResourceServerConfigurerAdapter { //访问规则在 configure() 方法中定义,并且通过传入方法的 HttpSecurity 对象配置 @Override public void configure(HttpSecurity http) throws Exception{ http.authorizeRequests().anyRequest().authenticated(); }}anyRequest().authenticated() 表示需要由已通过验证的用户访问;@Configurationpublic 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 保护的服务;
@Beanpublic OAuth2RestTemplate oauth2RestTemplate(OAuth2ClientContext oauth2ClientContext, OAuth2ProtectedResourceDetails details) { return new OAuth2RestTemplate(details, oauth2ClientContext);}@Componentpublic 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(); }}