上一篇搭建了一个 OAuth2 认证服务器, 可以生成 token, 这篇来改造下之前的订单微服务, 使其能够认这个 token 令牌.
本篇针对订单服务要做三件事:
1, 要让他知道自己是资源服务器, 他知道这件事后, 才会在前边加一个过滤器去验令牌 (配置 @EnableResourceServer 配置类)
2, 要让他知道自己是什么资源服务器 (配置资源服务器 ID)
3, 配置去哪里验令牌, 怎么验令牌, 要带什么信息去验 (配置 @EnablewebSecurity 配置 TokenServices, 配置 AuthenticationManager)
搭建资源服务器
++++++++++++++++++ 资源服务器现有的各个类 ++++++++++++++++++++++++++++
pom.xml:
- <?xml version="1.0" encoding="UTF-8"?>
- <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <groupId>com.nb.security</groupId>
- <artifactId>nb-order-API</artifactId>
- <version>0.0.1-SNAPSHOT</version>
- <properties>
- <java.version>1.8</java.version>
- </properties>
- <dependencyManagement>
- <dependencies>
- <dependency>
- <!-- Import dependency management from Spring Boot -->
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-dependencies</artifactId>
- <version>2.1.6.RELEASE</version>
- <type>pom</type>
- <scope>import</scope>
- </dependency>
- <!--spring cloud-->
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-dependencies</artifactId>
- <version>Greenwich.SR2</version>
- <type>pom</type>
- <scope>import</scope>
- </dependency>
- </dependencies>
- </dependencyManagement>
- <dependencies>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-Web</artifactId>
- </dependency>
- <!--OAuth2-->
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-OAuth2</artifactId>
- </dependency>
- <!--lombok-->
- <dependency>
- <groupId>org.projectlombok</groupId>
- <artifactId>lombok</artifactId>
- </dependency>
- </dependencies>
- <build>
- <plugins>
- <!-- 指定 JDK 编译版本 -->
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- <configuration>
- <source>1.8</source>
- <target>1.8</target>
- <encoding>UTF-8</encoding>
- </configuration>
- </plugin>
- <!-- 打包跳过测试 -->
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-surefire-plugin</artifactId>
- <configuration>
- <skipTests>true</skipTests>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-maven-plugin</artifactId>
- </plugin>
- </plugins>
- </build>
- </project>
- View Code
- application.YAML:
- server:
- port: 9060
- View Code
- OrderInfo.java :
- package com.nb.security.order;
- import lombok.AllArgsConstructor;
- import lombok.Data;
- import lombok.NoArgsConstructor;
- @Data
- @NoArgsConstructor
- @AllArgsConstructor
- public class OrderInfo {
- private Long productId;
- }
- View Code
- OrderController:
- package com.nb.security.order;
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.Web.bind.annotation.*;
- import org.springframework.Web.client.RestTemplate;
- @Slf4j
- @RestController
- @RequestMapping("/orders")
- public class OrderController {
- private RestTemplate restTemplate = new RestTemplate();
- @PostMapping
- public OrderInfo create(@RequestBody OrderInfo info){
- // 查询价格
- // PriceInfo price = restTemplate.getForObject("http://localhost:9080/prices/"+info.getProductId(),PriceInfo.class);
- // log.info("price is"+price.getPrice());
- return info;
- }
- @GetMapping
- public OrderInfo getInfo(@PathVariable Long id){
- log.info("getInfo: id is"+id);
- return new OrderInfo(id);
- }
- }
- View Code
启动类:
- package com.nb.security;
- import org.springframework.boot.SpringApplication;
- import org.springframework.boot.autoconfigure.SpringBootApplication;
- @SpringBootApplication
- public class NbOrderApiApplication {
- public static void main(String[] args) {
- SpringApplication.run(NbOrderApiApplication.class, args);
- }
- }
- View Code
- PriceInfo.java (暂时先不用):
- package com.nb.security.order;
- import lombok.Data;
- import java.math.BigDecimal;
- @Data
- public class PriceInfo {
- private Long id;
- private BigDecimal price;
- }
- View Code
++++++++++++++++++ 资源服务器现有的各个类结束 ++++++++++++++++++++++++++++
1, 在资源服务器 pom 哩加上 OAuth2 的依赖:
- <!--OAuth2-->
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-OAuth2</artifactId>
- </dependency>
2, 新建资源服务器配置类, 继承 ResourceServerConfigurerAdapter
- package com.nb.security.resource.server;
- 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;
- import org.springframework.security.OAuth2.config.annotation.Web.configurers.ResourceServerSecurityConfigurer;
- /**
- * 资源服务器
- * 配置了 @EnableResourceServer , 所有发往 nb-order-API 的请求, 都会去请求头里找 token, 找不到不让你过
- */
- @Configuration
- @EnableResourceServer// 告诉 nb-order-API, 你就是资源服务器
- public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {
- @Override
- public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
- // 配置资源服务器的 id,"现在我就是资源服务器 order-server!!!"
- resources.resourceId("order-server");
- }
- @Override
- public void configure(HttpSecurity http) throws Exception {
- /**
- * 进入 nb-order-API 的所有请求, 哪些要拦截, 哪些要放过, 在这里配置
- */
- http.authorizeRequests()
- .antMatchers("/hello")
- .permitAll() // 放过 / haha 不拦截
- .anyRequest().authenticated();// 其余所有请求都拦截
- }
- }
ResourceServerConfigurerAdapter 有两个方法:
开篇说的 1,2,3 中的 1 和 2 已经完成了, 下面做第 3 件事, 怎么验令牌:
新建配置类:
- package com.nb.security.resource.server;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.security.authentication.AuthenticationManager;
- import org.springframework.security.config.annotation.Web.configuration.EnableWebSecurity;
- import org.springframework.security.config.annotation.Web.configuration.WebSecurityConfigurerAdapter;
- import org.springframework.security.OAuth2.provider.authentication.OAuth2AuthenticationManager;
- import org.springframework.security.OAuth2.provider.token.RemoteTokenServices;
- import org.springframework.security.OAuth2.provider.token.ResourceServerTokenServices;
- /**
- * 怎么验发往本服务的请求头的令牌
- * 1, 自定义 tokenServices , 说明去哪里去验 token
- * 2, 重写 authenticationManagerBean() 方法, 将 AuthenticationManager 暴露为一个 Bean
- * 要认证跟用户相关的信息, 一般用 AuthenticationManager
- *
- * 这样配置了后, 所有发往 nb-order-API 的请求,
- * 需要验 token 的时候就会发请求去 http://localhost:9090/OAuth/check_token 验 token, 获取到 token 对应的用户信息
- */
- @Configuration
- @EnableWebSecurity
- public class OAuth2WebSecurityConfig extends WebSecurityConfigurerAdapter{
- /**
- * 通过这个 Bean, 去远程调用认证服务器, 验 token
- * @return
- */
- @Bean
- public ResourceServerTokenServices tokenServices(){
- RemoteTokenServices tokenServices = new RemoteTokenServices();
- tokenServices.setClientId("orderService");// 在认证服务器配置的, 订单服务的 clientId
- tokenServices.setClientSecret("123456");// 在认证服务器配置的, 订单服务的 ClientSecret
- tokenServices.setCheckTokenEndpointUrl("http://localhost:9090/oauth/check_token");
- return tokenServices;
- }
- /**
- * 要认证跟用户相关的信息, 一般用 AuthenticationManager
- * 覆盖这个方法, 可以将 AuthenticationManager 暴露为一个 Bean
- *
- * @return
- * @throws Exception
- */
- @Bean
- @Override
- public AuthenticationManager authenticationManagerBean() throws Exception {
- OAuth2AuthenticationManager authenticationManager = new OAuth2AuthenticationManager();
- authenticationManager.setTokenServices(tokenServices());// 设置为自定义的 TokenServices, 去校验令牌
- return authenticationManager;
- }
- }
认证服务器关于订单服务的配置:
启动认证服务器,
启动订单 资源服务器,
访问认证服务器获取 token :localhost:9090/OAuth/token
拿着 token 去资源服务器创建订单, 注意, 选择 bearer 类型的 token, 生成的请求头是这样的 (注: Authorization 的 value 值, postman 生成的是以 Bearer 开头的, 但是我看谷歌浏览器 restclient 插件生成的是 bearer 的 B 是小写):
在资源服务器里, 可以通过该注解获取用户名:
错误的情况:
如果把资源服务器配置的 resourceId 改成了 order-server222, 请求创建订单, 会受到如下的错误
如果资源服务器检验 token 的 cilentID 获取 clientSecret 写错了, 后台会报错:
++++++++++++++++++++++ 分割线 ++++++++++++++++++++++
小结:
接上篇的认证服务器, 本篇实现了资源服务器, 以及与认证服务器的交互, 怎么去验令牌.
遗留疑问:
认证服务器里面, 配置资源服务器 secret 的时候, 用 passwordEncoder 对 123456 进行了加密, 而在资源服务器里, clientSecret 确实明文的 123456, 这两者不需要一致么?
认证服务器里对资源服务器 ClientId, ClientSecret 配置:
资源服务器验令牌携带的 ClientId, ClientSecret:
代码 GitHub :
来源: http://www.bubuko.com/infodetail-3337002.html