OAuth2-授权码模式 登录流程

时间:2020-04-26
本文章向大家介绍OAuth2-授权码模式 登录流程,主要包括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"
}

原文地址:https://www.cnblogs.com/xp0813/p/12781646.html