zoukankan      html  css  js  c++  java
  • 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"
    }
  • 相关阅读:
    linux ftp启用和停用及vsftpd安装 ssh 启用和停用命令及ssh的安装
    linux 网络命令
    printf的使用
    Shell echo命令
    shell脚本之文件操作
    shell脚本之字符串运算的使用
    linux的计划任务操作
    系统操作有关的命令
    shell脚本之 operater.sh 算术运算符;比较运算符;布尔运算符;逻辑与或非运算符;字符串运算符的使用
    shell脚本的参数传递使用
  • 原文地址:https://www.cnblogs.com/xp0813/p/12781646.html
Copyright © 2011-2022 走看看