OAuth2-授權碼模式 登錄流程


一.案例架構

  主要包括如下服務:

    1.第三方應用

    2.授權服務器

    3.資源服務器

    4.用戶

項目 端口 備注
auth-server 8080 授權服務器
user-server 8081 資源服務器
client-app 8082 第三方應用

  首先來創建一個空的 Maven 父工程,創建好之后,里邊什么都不用加,也不用寫代碼。我們將在這個父工程中搭建這個子模塊

二.授權服務器搭建  

  首先我們搭建一個名為 auth-server 的授權服務,搭建的時候,選擇如下三個依賴:

    1.web

    2.spring cloud security

    3.spirng cloud OAuth2

  創建完成之后提供一個SpringSecurity的基本配置

      

在這里配置的,就是用戶的用戶名/密碼/角色信息。

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("test1")
                .password(new BCryptPasswordEncoder().encode("123456"))
                .roles("admin")
                .and()
                .withUser("test2")
                .password(new BCryptPasswordEncoder().encode("123456"))
                .roles("user");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable().formLogin();
    }
}

 

  配置授權服務器

@Configuration
public class AccessTokenConfig {
  //生成的 Token 要往哪里存儲 可以存在 Redis 中,也可以存在內存中,也可以結合 JWT 等等 @Bean TokenStore tokenStore() {
return new InMemoryTokenStore(); //它存在內存中 } }
// 這個類繼承AuthorizationServerConfigureAdapter 對授權服務器的詳細配置 @EnableAuthorizationServer //表示開啟授權服務器的自動化配置。 @Configuration
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter { @Autowired TokenStore tokenStore; @Autowired ClientDetailsService clientDetailsService;   
  //主要用來配置 Token 的一些基本信息 @Bean AuthorizationServerTokenServices tokenServices() { DefaultTokenServices services
= new DefaultTokenServices(); services.setClientDetailsService(clientDetailsService); services.setSupportRefreshToken(true); //是否支持刷新 services.setTokenStore(tokenStore); //token 存儲的位置 services.setAccessTokenValiditySeconds(60 * 60 * 2); //token的有效期 services.setRefreshTokenValiditySeconds(60 * 60 * 24 * 3); // 刷新token的有效期 return services; }
  //用來配置令牌端點的安全約束,也就是這個端點誰能訪問,誰不能訪問 @Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { security.checkTokenAccess("permitAll()") //checkTokenAccess 是指一個 Token 校驗的端點 .allowFormAuthenticationForClients(); //設置為可以直接訪問 當資源服務器收到 Token 之后,需要去校驗Token 的合法性,就會訪問這個端點 }
  //配置客戶端的詳細信息 @Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() //存儲到內存中 .withClient("test") //客戶端的id .secret(new BCryptPasswordEncoder().encode("123")) .resourceIds("rid") //資源id .authorizedGrantTypes("authorization_code","refresh_token") //授權類型 .scopes("all") //資源范圍 .redirectUris("http://localhost:8082/index.html"); //重定向url }
  //配置令牌的訪問端點和令牌服務 @Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.authorizationCodeServices(authorizationCodeServices()) //authorizationCodeService用來配置授權碼的存儲 這里我們是存在在內存中 .tokenServices(tokenServices()); //配置令牌的存儲 即 access_token 的存儲位置 } @Bean AuthorizationCodeServices authorizationCodeServices() { return new InMemoryAuthorizationCodeServices(); } }

  授權碼和令牌有什么區別授權碼是用來獲取令牌的,使用一次就失效,令牌則是用來獲取資源的

 

三.資源服務器搭建

  資源服務器就是用來存放用戶的資源,例如你在微信上的圖像、openid 等信息,用戶從授權服務器上拿到 access_token 之后,接下來就可以通過 access_token 來資源服務器請求數據。

  我們創建一個新的 Spring Boot 項目,叫做 user-server ,作為我們的資源服務器,創建時,添加如下依賴:

    1.web

    2.spring cloud security

    3.spirng cloud OAuth2

 

  配置資源服務器  (如果是小項目,資源和授權服務器可以放一起,大項目就要分開)

  

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

  //配置了一個 RemoteTokenServices 的實例 這是因為資源服務器和授權服務器是分開的,如果放一起就不需要配置 RemoteTokeService @Bean RemoteTokenServices tokenServices() { RemoteTokenServices services
= new RemoteTokenServices(); services.setCheckTokenEndpointUrl("http://localhost:8080/oauth/check_token"); //access_token 的校驗地址 services.setClientId("test"); //client_id services.setClientSecret("123"); //client_secret  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/**").hasRole("admin") .anyRequest().authenticated(); } }

測試接口

  

@RestController
public class HelloController {
    @GetMapping("/hello")
    public String hello() {
        return "hello";
    }
    @GetMapping("/admin/hello")
    public String admin() {
        return "admin";
    }
}

 

四.第三方應用搭建

  一個普通的 Spring Boot 工程,創建時加入 Thymeleaf 依賴和 Web 依賴:

  在 resources/templates 目錄下,創建 index.html ,內容如下:

  

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Oauth2</title>
</head>
<body>
你好,test!

<a href="http://localhost:8080/oauth/authorize?client_id=test&response_type=code&scope=all&redirect_uri=http://localhost:8082/index.html">第三方登錄</a>

<h1 th:text="${msg}"></h1>
</body>
</html>

 

@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", "test");
            map.add("client_secret", "123");
            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,也就是如果是通過授權服務器重定向到這個地址來的,那么我們做如下兩個操作:

  1. 根據拿到的 code,去請求 http://localhost:8080/oauth/token 地址去獲取 Token,返回的數據結構如下:

    

{
    "access_token": "e7f223c4-7543-43c0-b5a6-5011743b5af4",      //請求數據所需要的令牌
    "token_type": "bearer",
    "refresh_token": "aafc167b-a112-456e-bbd8-58cb56d915dd",     //刷新token所需要的令牌
    "expires_in": 7199,                                          //token有效期還剩多久
    "scope": "all"
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM