oauth2接入米家开发者
工具
- idea
- 花生壳(自己电脑web服务可以使用公网访问)
- 浏览器
- 米家企业开发者账号
代码
-
目录结构
-
pom
-
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.1</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>demo6</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demo6</name> <description>demo6</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth2</artifactId> <version>2.3.4.RELEASE</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project>
-
-
AuthServerConfiguration(资源认证服务器)
-
package com.example.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; @Configuration @EnableAuthorizationServer public class AuthServerConfiguration extends AuthorizationServerConfigurerAdapter { /** * 密码加密器 */ @Autowired private PasswordEncoder passwordEncoder; /** * 认证服务默认是post请求,允许可以通过get请求认证 * @param endpoints * @throws Exception */ @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST); } /** * 认证服务默认是basic认证,允许可以通过表单请求认证 * @param security * @throws Exception */ @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { security.allowFormAuthenticationForClients(); } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { // 配置米家 测试账号 clients.inMemory() .withClient("test") .secret(passwordEncoder.encode("123456")) .redirectUris("http://other") .authorizedGrantTypes("authorization_code","password","refresh_token","implicit","client_credentials") .scopes("all") .autoApprove(true) ; } }
-
-
ResourceServerConfiguration(资源保护过滤器)
-
package com.example.config; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; @Configuration @EnableResourceServer public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter { @Override public void configure(HttpSecurity http) throws Exception { http /** * 只拦截oauth2 请求需要认证服务验证 * 类似于 http://localhost:8080/oauth2/xxx?access_token=code */ .antMatcher("/oauth2/**") .authorizeRequests() // 其他请求 交给下一个拦截器 .anyRequest().authenticated(); } }
-
-
WebSecurityConfiguration(web请求拦截器)
-
package com.example.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.provisioning.InMemoryUserDetailsManager; @EnableWebSecurity @Configuration public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { /** * 密码加密 必须 * @return */ @Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } /** * 使用内存用户管理器创建两个用户 并且赋有两个角色 * @return */ @Override protected UserDetailsService userDetailsService() { InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager(); inMemoryUserDetailsManager.createUser(User.withUsername("admin").password(passwordEncoder().encode("123")).roles("admin").build()); inMemoryUserDetailsManager.createUser(User.withUsername("user").password(passwordEncoder().encode("123")).roles("user").build()); return inMemoryUserDetailsManager; } /** * 这个功能可以让输入的账号不需要转换加密 * @param auth * @throws Exception */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder()); } @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() // 处理user开头的需要user角色 .antMatchers("/user/**").hasRole("user") .antMatchers("/admin/**").hasRole("admin") // 以下请求放行 .antMatchers("/oauth/**","/login/**","/logout/**").permitAll() // 其他请求 放到别的过滤器中 .anyRequest() .authenticated() .and() // 使用表单验证账户 .formLogin() .permitAll() .and() .csrf().disable(); } }
-
测试
-
这里没有写接口请求返回,所以能返回404就是访问成功,别的就表明没有访问成功
-
http://localhost:8080/oauth2/test
- 会被资源保护器拦截
-
http://localhost:8080/user/test
-
会跳转到登录页面然后输入admin+123
-
会出现403没有角色
-
将网址改为/admin/test表示可以正常访问角色正确
-
-
http://localhost:8080/oauth/authorize?response_type=code&client_id=test&scope=all&redirect_uri=http://other
-
如果要访问/oauth2/**接口,先要获取请求令牌,跳出登录页面,随便登上一个账号admin+123会返回一个码gJNW6u
-
http://localhost:8080/oauth/token?grant_type=code&client_id=test&client_secret=123456&code=gJNW6u&redirect_uri=http://other (会得出我们所需要的access_token)
-
http://localhost:8080/oauth2/test?access_token=cd70264c-5c0f-4f39-9996-573021b83311 (我们能上面得到access_token cd70264c-5c0f-4f39-9996-573021b83311,然后再重新请求保护资源)
-
米家接入测试
-
启动花生壳,将服务映射出去,网上找教程,最终效果如下
-
然后打开米家开发平台,需要企业开发账号才可以选项
-
然后编辑配置,需要跟上面资源验证服务器代码,完全一致,这里要注意跳转地址,是米家开发给出的,要将代码中redirectUris()里面http://other改成https://oauth-redirect.api.home.mi.com/r/xxxxx,然后重启服务
-
然后重启idea服务.再返回米家,点击测试,输入账号密码点击login然后返回表示成功
总结
oauth/** 会被资源认证服务器拦截
oauth2/** 会被资源保护服务器拦截
别的请求 会被web请求服务器拦截