案例架构
| 项目 | 
端口 | 
备注 | 
| auth-server | 
8080 | 
授权服务器 | 
| resource-server | 
8081 | 
资源服务器 | 
| client-app | 
8082 | 
第三方应用 | 
授权服务器
创建SpringBoot应用并加入三个依赖:
- web
 
- spring cloud security
 
- spirng cloud OAuth2
 
1.对Spring Security做一些基本配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
   | @Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter {     @Bean     PasswordEncoder passwordEncoder() {         return new BCryptPasswordEncoder();     }
      @Override     protected void configure(AuthenticationManagerBuilder auth) throws Exception {         auth.inMemoryAuthentication()                 .withUser("atomsk")                 .password(new BCryptPasswordEncoder().encode("123"))                 .roles("admin")                 .and()                 .withUser("guest")                 .password(new BCryptPasswordEncoder().encode("123"))                 .roles("user");     }
      @Override     protected void configure(HttpSecurity http) throws Exception {         http.csrf().disable().formLogin();     } }
   | 
 
2.用Redis存储token
1 2 3 4 5 6 7 8 9 10
   | @Configuration public class AccessTokenConfig {     @Autowired     RedisConnectionFactory redisConnectionFactory;
      @Bean     TokenStore tokenStore(){         return new RedisTokenStore(redisConnectionFactory);     } }
   | 
 
3.配置授权服务器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
   | @Configuration @EnableAuthorizationServer public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {          @Autowired     TokenStore tokenStore;     @Autowired     ClientDetailsService clientDetailsService;
      @Bean     AuthorizationCodeServices authorizationCodeServices(){         
 
          return new InMemoryAuthorizationCodeServices();     }
      @Bean     AuthorizationServerTokenServices tokenServices() {         DefaultTokenServices services = new DefaultTokenServices();         services.setClientDetailsService(clientDetailsService);         services.setSupportRefreshToken(true);         services.setTokenStore(tokenStore);         services.setAccessTokenValiditySeconds(60 * 60 * 2);         services.setRefreshTokenValiditySeconds(60 * 60 * 24 * 3);         return services;     }
      @Override     public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {         security.checkTokenAccess("permitAll()")                 .allowFormAuthenticationForClients();     }
      @Override     public void configure(ClientDetailsServiceConfigurer clients) throws Exception {         clients.inMemory()                 .withClient("atomsk")                 .secret(new BCryptPasswordEncoder().encode("321"))                 .resourceIds("res1")                 .authorizedGrantTypes("authorization_code","refresh_token")                 .scopes("all")                 .redirectUris("http://localhost:8082/index.html");     }
      @Override     public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {         endpoints.authorizationCodeServices(authorizationCodeServices())                 .tokenServices(tokenServices());     } }
   | 
 
资源服务器
tip:小项目授权服务器和资源服务器可以放在一起。
和创建授权服务器一样,创建SpringBoot应用并加入三个依赖:
- web
 
- spring cloud security
 
- spirng cloud OAuth2
 
1.加入如下配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
   | @Configuration @EnableResourceServer public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
      @Bean     RemoteTokenServices tokenServices(){         RemoteTokenServices services=new RemoteTokenServices();         services.setCheckTokenEndpointUrl("http://localhost:8080/oauth/check_token");         services.setClientId("atomsk");         services.setClientSecret("321");         return services;     }
      @Override     public void configure(ResourceServerSecurityConfigurer resources) throws Exception {         resources.resourceId("res1").tokenServices(tokenServices());     }
      @Override     public void configure(HttpSecurity http) throws Exception {         http.authorizeRequests()                 .antMatchers("/admin/**").hasAnyRole("admin")                 .anyRequest().authenticated();     } }
   | 
 
2.添加两个配置接口
1 2 3 4 5 6 7 8 9 10 11
   | @RestController public class HelloController {     @GetMapping("/hello")     public String hello() {         return "hello";     }     @GetMapping("/admin/hello")     public String admin() {         return "admin";     } }
   | 
 
第三方应用
第三方应用就是一个普通的 Spring Boot 工程,创建时加入 Thymeleaf 依赖和 Web 依赖即可
1.在templates创建一个index.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14
   | <!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head>     <meta charset="UTF-8">     <title>Tittle</title> </head> <body> Hello,Oauth2      <a href="http://localhost:8080/oauth/authorize?client_id=atomsk&response_type=code">第三方登录</a>
  <h1 th:text="${msg}"></h1> </body> </html>
   | 
 
超链接里的参数:
- client_id 客户端 ID,根据我们在授权服务器中的实际配置填写。
 
- response_type 表示响应类型,这里是 code 表示响应一个授权码。
 
2.定一个Controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
   | @Controller public class HelloController {     @Autowired     RestTemplate restTemplate;
      @GetMapping("/index.html")     public String hello(String code, Model model) {         if (code != null) {             MultiValueMap<String, String> map = new LinkedMultiValueMap<>();             map.add("code", code);             map.add("client_id", "atomsk");             map.add("client_secret", "321");             map.add("redirect_uri", "http://localhost:8082/index.html");             map.add("grant_type", "authorization_code");             Map<String,String> resp = restTemplate.postForObject("http://localhost:8080/oauth/token", map, Map.class);             String access_token = resp.get("access_token");             System.out.println(access_token);             HttpHeaders headers = new HttpHeaders();             headers.add("Authorization", "Bearer " + access_token);             HttpEntity<Object> httpEntity = new HttpEntity<>(headers);             ResponseEntity<String> entity = restTemplate.exchange("http://localhost:8081/admin/hello", HttpMethod.GET, httpEntity, String.class);             model.addAttribute("msg", entity.getBody());         }         return "index";     } }
   | 
 
如果 code 不为 null,也就是如果是通过授权服务器重定向到这个地址来的,那么我们做如下两个操作:
根据拿到的 code,去请求 http://localhost:8080/oauth/token 地址去获取 Token,返回的数据结构如下:
1 2 3 4 5 6 7
   | {     "access_token": "e7f223c4-7543-43c0-b5a6-5011743b5af4",     "token_type": "bearer",     "refresh_token": "aafc167b-a112-456e-bbd8-58cb56d915dd",     "expires_in": 7199,     "scope": "all" }
  | 
 
 
接下来,根据我们拿到的 access_token,去请求资源服务器,注意 access_token 通过请求头传递,最后将资源服务器返回的数据放到 model 中
 
本博文完全参照江南一点雨的Oauth2的授权码模式案例写成,感兴趣的点击下面链接访问原文:
http://www.javaboy.org/2020/0414/oauth2_authorization_code.html