2021-11-13

代码:

AuthorizationServerConfig

@Configuration

@EnableAuthorizationServer

class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    //数据源,用于从数据库获取数据进行认证操作,测试可以从内存中获取

    @Autowired

    private DataSource dataSource;

    //jwt令牌转换器

    @Autowired

    private JwtAccessTokenConverter jwtAccessTokenConverter;

    //SpringSecurity 用户自定义授权认证类

    @Autowired

    UserDetailsService userDetailsService;

    //授权认证管理器

    @Autowired

    AuthenticationManager authenticationManager;

    //令牌持久化存储接口

    @Autowired

    TokenStore tokenStore;

    @Autowired

    private CustomUserAuthenticationConverter customUserAuthenticationConverter;

    /***

    * 客户端信息配置

    * @param clients

    * @throws Exception

    */

    @Override

    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {

        clients.jdbc(dataSource).clients(clientDetails());

      /*  clients.inMemory()

                .withClient("changgou")          //客户端id

                .secret("changgou")                      //秘钥

                .redirectUris("http://localhost")      //重定向地址

                .accessTokenValiditySeconds(3600)          //访问令牌有效期

                .refreshTokenValiditySeconds(3600)        //刷新令牌有效期

                .authorizedGrantTypes(

                        "authorization_code",          //根据授权码生成令牌

                        "client_credentials",          //客户端认证

                        "refresh_token",                //刷新令牌

                        "password")                    //密码方式认证

                .scopes("app");                        //客户端范围,名称自定义,必填*/

    }

    /***

    * 授权服务器端点配置

    * @param endpoints

    * @throws Exception

    */

    @Override

    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {

        endpoints.accessTokenConverter(jwtAccessTokenConverter)

                .authenticationManager(authenticationManager)//认证管理器

                .tokenStore(tokenStore)                      //令牌存储

                .userDetailsService(userDetailsService);    //用户信息service

    }

    /***

    * 授权服务器的安全配置

    * @param oauthServer

    * @throws Exception

    */

    @Override

    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {

        oauthServer.allowFormAuthenticationForClients()

                .passwordEncoder(new BCryptPasswordEncoder())

                .tokenKeyAccess("permitAll()")

                .checkTokenAccess("isAuthenticated()");

    }

    //读取密钥的配置

    @Bean("keyProp")

    public KeyProperties keyProperties(){

        return new KeyProperties();

    }

    @Resource(name = "keyProp")

    private KeyProperties keyProperties;

    //客户端配置

    @Bean

    public ClientDetailsService clientDetails() {

        return new JdbcClientDetailsService(dataSource);

    }

    @Bean

    @Autowired

    public TokenStore tokenStore(JwtAccessTokenConverter jwtAccessTokenConverter) {

        return new JwtTokenStore(jwtAccessTokenConverter);

    }

JwtAccessTokenConverter

    /****

    * JWT令牌转换器

    * @param customUserAuthenticationConverter

    * @return

    */

    @Bean

    public JwtAccessTokenConverter jwtAccessTokenConverter(CustomUserAuthenticationConverter customUserAuthenticationConverter) {

        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();

        KeyPair keyPair = new KeyStoreKeyFactory(

                keyProperties.getKeyStore().getLocation(),                          //证书路径 changgou.jks

                keyProperties.getKeyStore().getSecret().toCharArray())              //证书秘钥 changgouapp

                .getKeyPair(

                        keyProperties.getKeyStore().getAlias(),                    //证书别名 changgou

                        keyProperties.getKeyStore().getPassword().toCharArray());  //证书密码 changgou

        converter.setKeyPair(keyPair);

        //配置自定义的CustomUserAuthenticationConverter

        DefaultAccessTokenConverter accessTokenConverter = (DefaultAccessTokenConverter) converter.getAccessTokenConverter();

        accessTokenConverter.setUserTokenConverter(customUserAuthenticationConverter);

        return converter;

    }

}

@Component

public class CustomUserAuthenticationConverter extends DefaultUserAuthenticationConverter {

    @Autowired

    UserDetailsService userDetailsService;

    @Override

    public Map<String, ?> convertUserAuthentication(Authentication authentication) {

        LinkedHashMap response = new LinkedHashMap();

        String name = authentication.getName();

        response.put("username", name);

        Object principal = authentication.getPrincipal();

        UserJwt userJwt = null;

        if(principal instanceof  UserJwt){

            userJwt = (UserJwt) principal;

        }else{

            //refresh_token默认不去调用userdetailService获取用户信息,这里我们手动去调用,得到 UserJwt

            UserDetails userDetails = userDetailsService.loadUserByUsername(name);

            userJwt = (UserJwt) userDetails;

        }

        response.put("name", userJwt.getName());

        response.put("id", userJwt.getId());

        //公司 response.put("compy", "songsi");

        if (authentication.getAuthorities() != null && !authentication.getAuthorities().isEmpty()) {

            response.put("authorities", AuthorityUtils.authorityListToSet(authentication.getAuthorities()));

        }

        return response;

    }

}

UserDetailsServiceImpl

@Service

public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired

    ClientDetailsService clientDetailsService;

  @Autowired

  private UserFeign userFeign;

    /****

    * 自定义授权认证

    * @param username

    * @return

    * @throws UsernameNotFoundException

    */

    @Override

    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        //取出身份,如果身份为空说明没有认证

        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

        //没有认证统一采用httpbasic认证,httpbasic中存储了client_id和client_secret,开始认证client_id和client_secret

        if(authentication==null){

            ClientDetails clientDetails = clientDetailsService.loadClientByClientId(username);

            if(clientDetails!=null){

                //秘钥

                String clientSecret = clientDetails.getClientSecret();

                //静态方式

                //return new User(username,new BCryptPasswordEncoder().encode(clientSecret), AuthorityUtils.commaSeparatedStringToAuthorityList(""));

                //数据库查找方式

                return new User(username,clientSecret, AuthorityUtils.commaSeparatedStringToAuthorityList(""));

            }

        }

        if (StringUtils.isEmpty(username)) {

            return null;

        }

        //根据用户名查询用户信息

        //String pwd = new BCryptPasswordEncoder().encode("szitheima");

        String pwd = userFeign.findByUsername(username).getData().getPassword();

        //创建User对象  授予权限.GOODS_LIST  SECKILL_LIST

        String permissions = "goods_list,seckill_list";

        UserJwt userDetails = new UserJwt(username,pwd,AuthorityUtils.commaSeparatedStringToAuthorityList(permissions));

        //userDetails.setComy(songsi);

        return userDetails;

    }

    public static void main(String[] args) {

        String zhangsan = new BCryptPasswordEncoder().encode("zhangsan");

        System.out.println(zhangsan);

    }

}

WebSecurityConfig

@Configuration

@EnableWebSecurity

@Order(-1)

class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    /***

    * 忽略安全拦截的URL

    * @param web

    * @throws Exception

    */

    @Override

    public void configure(WebSecurity web) throws Exception {

        web.ignoring().antMatchers(

                "/user/login",

                "/user/logout","/oauth/login","/css/**","/data/**","/fonts/**","/img/**","/js/**");

    }

    /***

    * 创建授权管理认证对象

    * @return

    * @throws Exception

    */

    @Bean

    @Override

    public AuthenticationManager authenticationManagerBean() throws Exception {

        AuthenticationManager manager = super.authenticationManagerBean();

        return manager;

    }

    @Override

    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        super.configure(auth);

    }

    /***

    * 采用BCryptPasswordEncoder对密码进行编码

    * @return

    */

    @Bean

    public PasswordEncoder passwordEncoder() {

        return new BCryptPasswordEncoder();

    }

    /****

    *

    * @param http

    * @throws Exception

    */

    @Override

    public void configure(HttpSecurity http) throws Exception {

        http.csrf().disable()

                .httpBasic()        //启用Http基本身份验证

                .and()

                .formLogin()      //启用表单身份验证

                .loginPage("/oauth/login")

                .loginProcessingUrl("/user/login")

                .and()

                .authorizeRequests()    //限制基于Request请求访问

                .anyRequest()

                .authenticated();      //其他请求都需要经过验证

    }

}

AuthController

/*****

* @Author: www.itheima

* @Date: 2019/7/7 16:42

* @Description: com.changgou.oauth.controller

****/

@RestController

@RequestMapping(value = "/userx")

public class AuthController {

    //客户端ID

    @Value("${auth.clientId}")

    private String clientId;

    //秘钥

    @Value("${auth.clientSecret}")

    private String clientSecret;

    //Cookie存储的域名

    @Value("${auth.cookieDomain}")

    private String cookieDomain;

    //Cookie生命周期

    @Value("${auth.cookieMaxAge}")

    private int cookieMaxAge;

    @Autowired

    AuthService authService;

    @PostMapping("/login")

    public Result login(String username, String password) {

        if(StringUtils.isEmpty(username)){

            throw new RuntimeException("用户名不允许为空");

        }

        if(StringUtils.isEmpty(password)){

            throw new RuntimeException("密码不允许为空");

        }

        //申请令牌

        AuthToken authToken =  authService.login(username,password,clientId,clientSecret);

        //用户身份令牌

        String access_token = authToken.getAccessToken();

        //将令牌存储到cookie

        saveCookie(access_token);

        return new Result(true, StatusCode.OK,"登录成功!");

    }

    /***

    * 将令牌存储到cookie

    * @param token

    */

    private void saveCookie(String token){

        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();

        CookieUtil.addCookie(response,cookieDomain,"/","Authorization",token,cookieMaxAge,false);

    }

}

LoginRedirectController

@Controller

@RequestMapping("/oauth")

public class LoginRedirectController {

    @RequestMapping("/login")

    public String login(String From, Model model) {

        model.addAttribute("from",From);

        return "login";

    }

}

AuthServiceImpl

@Service

public class AuthServiceImpl implements AuthService {

    @Autowired

    private LoadBalancerClient loadBalancerClient;

    @Autowired

    private RestTemplate restTemplate;

    /***

    * 授权认证方法

    * @param username

    * @param password

    * @param clientId

    * @param clientSecret

    * @return

    */

    @Override

    public AuthToken login(String username, String password, String clientId, String clientSecret) {

        //申请令牌

        AuthToken authToken = applyToken(username,password,clientId, clientSecret);

        if(authToken == null){

            throw new RuntimeException("申请令牌失败");

        }

        return authToken;

    }

    /****

    * 认证方法

    * @param username:用户登录名字

    * @param password:用户密码

    * @param clientId:配置文件中的客户端ID

    * @param clientSecret:配置文件中的秘钥

    * @return

    */

    private AuthToken applyToken(String username, String password, String clientId, String clientSecret) {

        //选中认证服务的地址

        ServiceInstance serviceInstance = loadBalancerClient.choose("user-auth");

        if (serviceInstance == null) {

            throw new RuntimeException("找不到对应的服务");

        }

        //获取令牌的url

        String path = serviceInstance.getUri().toString() + "/oauth/token";

        //定义body

        MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();

        //授权方式

        formData.add("grant_type", "password");

        //账号

        formData.add("username", username);

        //密码

        formData.add("password", password);

        //定义头

        MultiValueMap<String, String> header = new LinkedMultiValueMap<>();

        header.add("Authorization", httpbasic(clientId, clientSecret));

        //指定 restTemplate当遇到400或401响应时候也不要抛出异常,也要正常返回值

        restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {

            @Override

            public void handleError(ClientHttpResponse response) throws IOException {

                //当响应的值为400或401时候也要正常响应,不要抛出异常

                if (response.getRawStatusCode() != 400 && response.getRawStatusCode() != 401) {

                    super.handleError(response);

                }

            }

        });

        Map map = null;

        try {

            //http请求spring security的申请令牌接口

            ResponseEntity<Map> mapResponseEntity = restTemplate.exchange(path, HttpMethod.POST,new HttpEntity<MultiValueMap<String, String>>(formData, header), Map.class);

            //获取响应数据

            map = mapResponseEntity.getBody();

        } catch (RestClientException e) {

            throw new RuntimeException(e);

        }

        if(map == null || map.get("access_token") == null || map.get("refresh_token") == null || map.get("jti") == null) {

            //jti是jwt令牌的唯一标识作为用户身份令牌

            throw new RuntimeException("创建令牌失败!");

        }

        //将响应数据封装成AuthToken对象

        AuthToken authToken = new AuthToken();

        //访问令牌(jwt)

        String accessToken = (String) map.get("access_token");

        //刷新令牌(jwt)

        String refreshToken = (String) map.get("refresh_token");

        //jti,作为用户的身份标识

        String jwtToken= (String) map.get("jti");

        authToken.setJti(jwtToken);

        authToken.setAccessToken(accessToken);

        authToken.setRefreshToken(refreshToken);

        return authToken;

    }

    /***

    * base64编码

    * @param clientId

    * @param clientSecret

    * @return

    */

    private String httpbasic(String clientId,String clientSecret){

        //将客户端id和客户端密码拼接,按“客户端id:客户端密码”

        String string = clientId+":"+clientSecret;

        //进行base64编码

        byte[] encode = Base64Utils.encode(string.getBytes());

        return "Basic "+new String(encode);

    }

}

LoginServiceImpl

@Service

public class LoginServiceImpl implements LoginService {

    @Autowired

    private RestTemplate restTemplate;

    @Autowired

    private LoadBalancerClient loadBalancerClient;

    @Override

    public AuthToken login(String username, String password, String clientId, String clientSecret, String grandType) {

        //1.定义url (申请令牌的url)

        //参数 : 微服务的名称spring.appplication指定的名称

        ServiceInstance choose = loadBalancerClient.choose("user-auth");

        String url =choose.getUri().toString()+"/oauth/token";

        //2.定义头信息 (有client id 和client secr)

        MultiValueMap<String,String> headers = new LinkedMultiValueMap<>();

        headers.add("Authorization","Basic "+Base64.getEncoder().encodeToString(new String(clientId+":"+clientSecret).getBytes()));

        //3. 定义请求体  有授权模式 用户的名称 和密码

        MultiValueMap<String,String> formData = new LinkedMultiValueMap<>();

        formData.add("grant_type",grandType);

        formData.add("username",username);

        formData.add("password",password);

        //4.模拟浏览器 发送POST 请求 携带 头 和请求体 到认证服务器

        /**

        * 参数1  指定要发送的请求的url

        * 参数2  指定要发送的请求的方法 PSOT

        * 参数3 指定请求实体(包含头和请求体数据)

        */

        HttpEntity<MultiValueMap> requestentity = new HttpEntity<MultiValueMap>(formData,headers);

        ResponseEntity<Map> responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestentity, Map.class);

        //5.接收到返回的响应(就是:令牌的信息)

        Map body = responseEntity.getBody();

        //封装一次.

        AuthToken authToken = new AuthToken();

        //访问令牌(jwt)

        String accessToken = (String) body.get("access_token");

        //刷新令牌(jwt)

        String refreshToken = (String) body.get("refresh_token");

        //jti,作为用户的身份标识

        String jwtToken= (String) body.get("jti");

        authToken.setJti(jwtToken);

        authToken.setAccessToken(accessToken);

        authToken.setRefreshToken(refreshToken);

        //6.返回

        return authToken;

    }

    public static void main(String[] args) {

        byte[] decode = Base64.getDecoder().decode(new String("Y2hhbmdnb3UxOmNoYW5nZ291Mg==").getBytes());

        System.out.println(new String(decode));

    }

}

public interface AuthService {

    /***

    * 授权认证方法

    */

    AuthToken login(String username, String password, String clientId, String clientSecret);

}

public interface LoginService {

    /**

    * 模拟用户的行为 发送请求 申请令牌 返回

    * @param username

    * @param password

    * @param clientId

    * @param clientSecret

    * @param grandType

    * @return

    */

    AuthToken login(String username, String password, String clientId, String clientSecret, String grandType);

}

AuthToken

public class AuthToken implements Serializable{

    //令牌信息

    String accessToken;

    //刷新token(refresh_token)

    String refreshToken;

    //jwt短令牌

    String jti;

    public String getAccessToken() {

        return accessToken;

    }

    public void setAccessToken(String accessToken) {

        this.accessToken = accessToken;

    }

    public String getRefreshToken() {

        return refreshToken;

    }

    public void setRefreshToken(String refreshToken) {

        this.refreshToken = refreshToken;

    }

    public String getJti() {

        return jti;

    }

    public void setJti(String jti) {

        this.jti = jti;

    }

}

CookieUtil

public class CookieUtil {

    /**

    * 设置cookie

    *

    * @param response

    * @param name    cookie名字

    * @param value    cookie值

    * @param maxAge  cookie生命周期 以秒为单位

    */

    public static void addCookie(HttpServletResponse response, String domain, String path, String name,

                                String value, int maxAge, boolean httpOnly) {

        Cookie cookie = new Cookie(name, value);

        cookie.setDomain(domain);

        cookie.setPath(path);

        cookie.setMaxAge(maxAge);

        cookie.setHttpOnly(httpOnly);

        response.addCookie(cookie);

    }

    /**

    * 根据cookie名称读取cookie

    * @param request

    * @return map<cookieName,cookieValue>

    */

    public static Map<String,String> readCookie(HttpServletRequest request, String ... cookieNames) {

        Map<String,String> cookieMap = new HashMap<String,String>();

            Cookie[] cookies = request.getCookies();

            if (cookies != null) {

                for (Cookie cookie : cookies) {

                    String cookieName = cookie.getName();

                    String cookieValue = cookie.getValue();

                    for(int i=0;i<cookieNames.length;i++){

                        if(cookieNames[i].equals(cookieName)){

                            cookieMap.put(cookieName,cookieValue);

                        }

                    }

                }

            }

        return cookieMap;

    }

}

OAuthApplication

@SpringBootApplication

@EnableDiscoveryClient

@MapperScan(basePackages = "com.changgou.auth.dao")

@EnableFeignClients(basePackages = {"com.changgou.user.feign"})

public class OAuthApplication {

    public static void main(String[] args) {

        SpringApplication.run(OAuthApplication.class,args);

    }

    @Bean(name = "restTemplate")

    public RestTemplate restTemplate() {

        return new RestTemplate();

    }

}

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 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <parent>

        <artifactId>changgou-parent</artifactId>

        <groupId>com.changgou</groupId>

        <version>1.0-SNAPSHOT</version>

    </parent>

    <modelVersion>4.0.0</modelVersion>

    <artifactId>changgou-user-oauth</artifactId>

    <description>

        OAuth2.0认证环境搭建

    </description>

    <dependencies>

        <!--查询数据库数据-->

        <dependency>

            <groupId>com.changgou</groupId>

            <artifactId>changgou-common-db</artifactId>

            <version>1.0-SNAPSHOT</version>

        </dependency>

        <dependency>

            <groupId>org.springframework.security</groupId>

            <artifactId>spring-security-data</artifactId>

        </dependency>

        <dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-starter-oauth2</artifactId>

        </dependency>

        <dependency>

            <groupId>org.springframework.cloud</groupId>

            <artifactId>spring-cloud-starter-security</artifactId>

        </dependency>

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-actuator</artifactId>

        </dependency>

        <dependency>

            <groupId>com.changgou</groupId>

            <artifactId>changgou-service-user-api</artifactId>

            <version>1.0-SNAPSHOT</version>

        </dependency>

        <!--thymleaf的依赖-->

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-thymeleaf</artifactId>

        </dependency>

    </dependencies>

</project>

application.yml

server:

  port: 9001

spring:

  application:

    name: user-auth

  redis:

    host: 192.168.211.132

    port: 6379

    password:

    jedis:

      pool:

        max-active: 8

        max-idle: 8

        min-idle: 0

  datasource:

    driver-class-name: com.mysql.jdbc.Driver

    url: jdbc:mysql://192.168.211.132:3306/changgou_oauth?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&serverTimezone=UTC

    username: root

    password: 123456

  main:

    allow-bean-definition-overriding: true

  thymeleaf:

    cache: false

eureka:

  instance:

    prefer-ip-address: true

  client:

    service-url:

      defaultZone: http://127.0.0.1:7001/eureka

auth:

  ttl: 3600  #token存储到redis的过期时间

  clientId: changgou

  clientSecret: changgou

  cookieDomain: localhost

  cookieMaxAge: -1

encrypt:

  key-store:

    location: classpath:/changgou.jks

    secret: changgou

    alias: changgou

password: changgou

脚本:

申请授权码

GET

http://localhost:9001/oauth/authorize?client_id=changgou&response_type=code&scop=app&redirect_uri=http://localhost

根据授权码获取token

POST

http://localhost:9001/oauth/token

Authorization: username  password  (Basic Auth)

form-data:

grant_type authorization_code

Code kKghj4

redirect_uri  http://localhost

刷新令牌:

POST

http://localhost:9001/oauth/token?grant_type=refresh_token&refresh_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6WyJhcHAiXSwiYXRpIjoiMjNmYTQwOGEtYzkyMi00NDdmLTk3OTktNjY2NjEzNjEwNTM5IiwibmFtZSI6bnVsbCwiaWQiOm51bGwsImV4cCI6MTYzNDQ4OTA4MCwiYXV0aG9yaXRpZXMiOlsic2Vja2lsbF9saXN0IiwiZ29vZHNfbGlzdCJdLCJqdGkiOiI0ZjU3MWU5ZS05MGMxLTQ0YzMtOTI1ZS05OTRjODQ4MmQ2OWMiLCJjbGllbnRfaWQiOiJjaGFuZ2dvdSIsInVzZXJuYW1lIjoic3ppdGhlaW1hIn0.MdkCkvQidZ2kJ6YC45-JDtr1sMSRmlEAZH5Vj3IkgLxvLFf5mktPqYMMFLS5K5yut53G5gBRCy792ZYbkC0sT-y5tHAnpGrTwLJPG6ZUI8dr5t2UotX4xDv_zlo4DQiUfmG2kBD8hKQeBaa6Y5WdWndfq11sNmdwZzFIBkGsgT-I15IXtx3UEL5y-kMQ30xpBJG_OlGE9y8RCi1H0Xa-kEPNVvkqYWwawW9mBgasH57yeYTEWYhgVCmYmLV3YZdQOu58JqwHKcFeUwUSya77m2Rr157uOZjxTk3tSoBtUAqLivJNU8XmRAxT5UIU4S5Sv7Egzm3uHjL7LWLKuCZ40g

令牌校验:

POST

http://localhost:9001/oauth/check_token?token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6WyJhcHAiXSwibmFtZSI6bnVsbCwiaWQiOm51bGwsImV4cCI6MTYzNDQ4OTA4MCwiYXV0aG9yaXRpZXMiOlsic2Vja2lsbF9saXN0IiwiZ29vZHNfbGlzdCJdLCJqdGkiOiIyM2ZhNDA4YS1jOTIyLTQ0N2YtOTc5OS02NjY2MTM2MTA1MzkiLCJjbGllbnRfaWQiOiJjaGFuZ2dvdSIsInVzZXJuYW1lIjoic3ppdGhlaW1hIn0.BlU5X5ZkJWq0X-AT1Qjg3eRkhvBvtgqpkW6twqn1sM0diXxjlxoZsOJspqHDFlbg2nrgv0uGMohEOhd8-1tLTZyttCTCqRaEkIsx-8u-vQmhmk9WqguGTjXKWZZhYAiXhlfg4SntMiAHYGmSyhoXAaH8GKifQb6Kyp-ChSAvAnwKzkQKNoMdXVTNyGlEn70ZCXw8gMwyJT9DH1Tr3o-sjyZ9Cq6uk4xMC7u-rypgJp2DsedCQo9wh5QH4PlD-QzqJq9FWsPFJnLptDYcXg9wIPRzmQnT33QqG9pDMHo2gCXuIa3oZlTylWcwFyt9ERohafZcbGvQQemp5kwEgQnjDQ

http://localhost:9001/oauth/token

Post

Formdata:

grant_type password

Username szitheima

Password szitheima

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容