Merge branch 'dev' into feature-emqx

# Conflicts:
#	manager/src/main/resources/application.yml
V0.5.x
七步才子 2022-05-21 18:15:21 +08:00
commit f025375497
37 changed files with 388 additions and 306 deletions

134
README.md
View File

@ -13,6 +13,11 @@
**智能家居小程序:** https://gitee.com/iotkit-open-source/iot-mp-home
**系统截图**
![输入图片说明](doc/WechatIMG538.png)
![输入图片说明](doc/WechatIMG539.png)
#### 软件架构
软件架构说明
本系统采用springboot、mongodb、redis、elasticsearch、pulsar、sa-token等框架和第三方软件
@ -20,7 +25,101 @@
#### 安装教程
https://ztktkv.yuque.com/docs/share/b32da919-0108-4112-9406-fe5c6672e0d7?# 《安装和配置》
**application-dev.yml配置**
```
server:
port: 8086
spring:
servlet:
multipart:
enabled: true
max-file-size: 10MB
max-request-size: 12MB
#mongodb数据库配置
data:
mongodb:
uri: mongodb://填写mongodb地址
database: iotkit
elasticsearch:
rest:
#使用内置es的配置
uris: http://127.0.0.1:9200
username:
password:
connection-timeout: 10s
redis:
#使用内置redis的配置
host: 127.0.0.1
port: 6379
database: 0
password:
mvc:
pathmatch:
matching-strategy: ant_path_matcher
#图片存储用的是阿里云oss如果需要上传产品图片才需要配置
aliyun:
bucketId: 填写阿里云bucketId
endpoint: oss-cn-shenzhen.aliyuncs.com
accessKeyId: 填写阿里云accessKeyId
accessKeySecret: 填写阿里云accessKeySecret
sa-token:
# token名称 (同时也是cookie名称)
token-name: satoken
# token有效期单位s 默认30天, -1代表永不过期
timeout: 2592000
# token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒
activity-timeout: -1
# 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
is-concurrent: true
# 在多人登录同一账号时是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
is-share: false
# token风格
token-style: uuid
# 是否输出操作日志
is-log: false
#pulsar消息队列配置,需要进行配置
pulsar:
broker: pulsar://pulsar broker地址:6650
service: http://pulsar 服务地址:8080
#认证中心地址
oauth2:
auth-server-url: http://127.0.0.1:8086
```
**启用内置es和redis配置**
增加启动参数
```
embeddedElasticSearch=true
embeddedRedisServer=true
```
如idea中
![输入图片说明](doc/WX20220519-131333@2x.png)
**mongodb数据库示例脚本**
请加QQ或微信群获取
以上最小配置项pulsar消息队列配置、mongodb数据库配置。
monogodb和pulsar安装配置见末尾。
#### 使用说明
@ -28,22 +127,26 @@
邀你加入「iot平台技术文档」知识库: https://ztktkv.yuque.com/g/ztktkv/gb3v6g/collaborator/join?token=zz5PUmXzGQqc4h9t#
**这是我宝贵的技术文档分享请给本仓库点个star :sparkles: 支持一下,谢谢!**
2. 系统操作文档
2. 系统操作说明
**开启MQTT标准协议接入功能**
在消息转换器管理中编辑”奇特MQTT标准协议“编辑转换脚本示例脚本进群获取
启用MQTT标准协议组件
在组件管理中修改”MQTT标准协议组件“上传组件jar包将mqtt-component项目执行打包生成的mqtt-component-xx.jar上传然后再编写脚本示例脚本进群获取然后点击”状态“启用组件。
**使用模拟器进行设备测试**
启动项目中的mqtt-client-simulator模拟器模拟设备接入系统
#### 待办事项
- ->告警中心
- 数据大屏
#### 参与贡献
1. Fork 本仓库
2. 新建 Feat_xxx 分支
3. 提交代码
4. 新建 Pull Request
#### 捐助与支持
如果您觉得我的开源软件对你有所帮助请打个赏1元钱也是爱。
@ -54,3 +157,12 @@
微信群:
![输入图片说明](doc/ma.png)
**mongodb安装配置**
![输入图片说明](doc/mongodb%E5%AE%89%E8%A3%85%E9%85%8D%E7%BD%AE.jpg)
**Pulsar安装配置**
![输入图片说明](doc/%E5%AE%89%E8%A3%85Pulsar.jpg)

View File

@ -1,6 +1,7 @@
package cc.iotkit.common.utils;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import javax.crypto.Cipher;
@ -96,4 +97,9 @@ public class CodecUtil {
encryptStr = new String(HexUtil.parseHex(encryptStr));
return StringUtils.isEmpty(encryptStr) ? "" : aesDecryptByBytes(base64Decode(encryptStr), decryptKey);
}
public static String md5Str(String content) {
return DigestUtils.md5Hex(content);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

BIN
doc/WechatIMG538.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

BIN
doc/WechatIMG539.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 KiB

BIN
doc/mongodb安装配置.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 497 KiB

BIN
doc/安装Pulsar.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

View File

@ -1,22 +0,0 @@
package cc.iotkit.manager.config;
import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.KeycloakDeploymentBuilder;
import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver;
import org.keycloak.representations.adapters.config.AdapterConfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AutoBeanConfig {
@Bean
public KeycloakSpringBootConfigResolver keycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
@Bean
public KeycloakDeployment getKeycloakDeployment(AdapterConfig adapterConfig){
return KeycloakDeploymentBuilder.build(adapterConfig);
}
}

View File

@ -1,92 +0,0 @@
package cc.iotkit.manager.config;
import org.keycloak.adapters.springsecurity.KeycloakConfiguration;
import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider;
import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;
import org.keycloak.adapters.springsecurity.management.HttpSessionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
@EnableGlobalMethodSecurity(prePostEnabled = true)
@KeycloakConfiguration
public class KeycloakSecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
@Value("${app.systemRole}")
private String systemRole;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
SimpleAuthorityMapper grantedAuthorityMapper = new SimpleAuthorityMapper();
grantedAuthorityMapper.setPrefix("ROLE_");
KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(grantedAuthorityMapper);
auth.authenticationProvider(keycloakAuthenticationProvider);
}
@Bean
@Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
// return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
return new NullAuthenticatedSessionStrategy();
}
@Bean
@Override
@ConditionalOnMissingBean(HttpSessionManager.class)
protected HttpSessionManager httpSessionManager() {
return new HttpSessionManager();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http
.authorizeRequests()
.antMatchers("/*.html", "/favicon.ico", "/v2/api-docs", "/webjars/**", "/swagger-resources/**", "/*.js").permitAll()
.antMatchers("/api/**").hasRole("iot_client_user")
.antMatchers("/aligenieDevice/invoke/**").hasRole("iot_client_user")
//客户端用户写权限
.antMatchers("/space/addSpace/**").hasAnyRole("iot_write","iot_client_user")
.antMatchers("/space/saveSpace/**").hasAnyRole("iot_write","iot_client_user")
.antMatchers("/space/delSpace/**").hasAnyRole("iot_write","iot_client_user")
.antMatchers("/space/saveHome/**").hasAnyRole("iot_write","iot_client_user")
.antMatchers("/space/currentHome/**").hasAnyRole("iot_write","iot_client_user")
.antMatchers("/space/myRecentDevices/**").hasAnyRole("iot_write","iot_client_user")
.antMatchers("/space/spaces/**").hasAnyRole("iot_write","iot_client_user")
.antMatchers("/space/myDevices/**").hasAnyRole("iot_write","iot_client_user")
.antMatchers("/space/findDevice/**").hasAnyRole("iot_write","iot_client_user")
.antMatchers("/space/addDevice/**").hasAnyRole("iot_write","iot_client_user")
.antMatchers("/space/saveDevice").hasAnyRole("iot_write","iot_client_user")
.antMatchers("/space/removeDevice").hasAnyRole("iot_write","iot_client_user")
.antMatchers("/space/device/*").hasAnyRole("iot_write","iot_client_user")
.antMatchers("/device/*/consumer/*").hasAnyRole("iot_write","iot_client_user")
.antMatchers("/device/*/service/property/set").hasAnyRole("iot_write","iot_client_user")
.antMatchers("/device/*/service/*/invoke").hasAnyRole("iot_write","iot_client_user")
.antMatchers(HttpMethod.DELETE).hasRole("iot_write")
.antMatchers(HttpMethod.PUT).hasRole("iot_write")
.antMatchers("/**/save*/**").hasRole("iot_write")
.antMatchers("/**/remove*/**").hasRole("iot_write")
.antMatchers("/**/del*/**").hasRole("iot_write")
.antMatchers("/**/add*/**").hasRole("iot_write")
.antMatchers("/**/clear*/**").hasRole("iot_write")
.antMatchers("/**/set*/**").hasRole("iot_write")
.antMatchers("/**/set").hasRole("iot_write")
.antMatchers("/**/invoke").hasRole("iot_write")
.antMatchers("/**").hasAnyRole(systemRole)
.and().csrf().disable();
}
}

View File

@ -4,10 +4,12 @@ import cn.dev33.satoken.interceptor.SaAnnotationInterceptor;
import cn.dev33.satoken.interceptor.SaRouteInterceptor;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Slf4j
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {
@ -17,23 +19,8 @@ public class SaTokenConfigure implements WebMvcConfigurer {
registry.addInterceptor(new SaAnnotationInterceptor()).addPathPatterns("/**");
// 注册路由拦截器,自定义认证规则
registry.addInterceptor(new SaRouteInterceptor((req, res, handler) -> {
System.out.println(req.getRequestPath());
// 根据路由划分模块,不同模块不同鉴权
log.info("resource role check,path:{}", req.getRequestPath());
SaRouter
//管理员、系统用户角色能使用的功能
.match("/**")
.notMatch("/oauth2/**","/*.png").check(c -> StpUtil.checkRoleOr("iot_admin", "iot_system"))
//需要有可写权限的功能
.match(
"/**/save*/**",
"/**/remove*/**",
"/**/del*/**",
"/**/add*/**",
"/**/clear*/**",
"/**/set*/**",
"/**/set",
"/**/invoke"
).check(c -> StpUtil.checkPermission("write"))
//管理员、系统、客户端用户角色能使用的功能
.match("/space/addSpace/**",
"/space/saveSpace/**",
@ -52,7 +39,26 @@ public class SaTokenConfigure implements WebMvcConfigurer {
"/device/*/service/property/set",
"/device/*/service/*/invoke"
)
.check(c -> StpUtil.checkRoleOr("iot_admin", "iot_system", "iot_client"));
SaRouter
//需要有可写权限的功能
.match(
"/**/save*/**",
"/**/remove*/**",
"/**/del*/**",
"/**/add*/**",
"/**/clear*/**",
"/**/set*/**",
"/**/set",
"/**/invoke"
).check(c -> StpUtil.checkPermission("write"));
SaRouter
//管理员、系统用户角色能使用的功能
.match("/**")
.check(c -> StpUtil.checkRoleOr("iot_admin", "iot_system", "iot_client"))
;
})).addPathPatterns("/**")
.excludePathPatterns(

View File

@ -1,55 +0,0 @@
package cc.iotkit.manager.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Value("${app.systemRole}")
private String systemRole;
@Override
protected void configure(HttpSecurity http) throws Exception {
// super.configure(http);
http
.authorizeRequests()
.antMatchers("/oauth2/**", "/*.html", "/favicon.ico", "/v2/api-docs", "/webjars/**", "/swagger-resources/**", "/*.js").permitAll()
.antMatchers("/api/**").hasRole("iot_client_user")
.antMatchers("/aligenieDevice/invoke/**").hasRole("iot_client_user")
//客户端用户写权限
.antMatchers("/space/addSpace/**").hasAnyRole("iot_write", "iot_client_user")
.antMatchers("/space/saveSpace/**").hasAnyRole("iot_write", "iot_client_user")
.antMatchers("/space/delSpace/**").hasAnyRole("iot_write", "iot_client_user")
.antMatchers("/space/saveHome/**").hasAnyRole("iot_write", "iot_client_user")
.antMatchers("/space/currentHome/**").hasAnyRole("iot_write", "iot_client_user")
.antMatchers("/space/myRecentDevices/**").hasAnyRole("iot_write", "iot_client_user")
.antMatchers("/space/spaces/**").hasAnyRole("iot_write", "iot_client_user")
.antMatchers("/space/myDevices/**").hasAnyRole("iot_write", "iot_client_user")
.antMatchers("/space/findDevice/**").hasAnyRole("iot_write", "iot_client_user")
.antMatchers("/space/addDevice/**").hasAnyRole("iot_write", "iot_client_user")
.antMatchers("/space/saveDevice").hasAnyRole("iot_write", "iot_client_user")
.antMatchers("/space/removeDevice").hasAnyRole("iot_write", "iot_client_user")
.antMatchers("/space/device/*").hasAnyRole("iot_write", "iot_client_user")
.antMatchers("/device/*/consumer/*").hasAnyRole("iot_write", "iot_client_user")
.antMatchers("/device/*/service/property/set").hasAnyRole("iot_write", "iot_client_user")
.antMatchers("/device/*/service/*/invoke").hasAnyRole("iot_write", "iot_client_user")
.antMatchers(HttpMethod.DELETE).hasRole("iot_write")
.antMatchers(HttpMethod.PUT).hasRole("iot_write")
.antMatchers("/**/save*/**").hasRole("iot_write")
.antMatchers("/**/remove*/**").hasRole("iot_write")
.antMatchers("/**/del*/**").hasRole("iot_write")
.antMatchers("/**/add*/**").hasRole("iot_write")
.antMatchers("/**/clear*/**").hasRole("iot_write")
.antMatchers("/**/set*/**").hasRole("iot_write")
.antMatchers("/**/set").hasRole("iot_write")
.antMatchers("/**/invoke").hasRole("iot_write")
.antMatchers("/**").hasAnyRole(systemRole)
.and().csrf().disable();
}
}

View File

@ -10,7 +10,7 @@ import cc.iotkit.manager.model.query.DeviceQuery;
import cc.iotkit.manager.service.DataOwnerService;
import cc.iotkit.manager.service.DeferredDataConsumer;
import cc.iotkit.manager.service.DeviceService;
import cc.iotkit.manager.utils.AuthUtil;
import cc.iotkit.utils.AuthUtil;
import cc.iotkit.model.InvokeResult;
import cc.iotkit.model.Paging;
import cc.iotkit.model.device.DeviceInfo;

View File

@ -9,7 +9,7 @@ import cc.iotkit.dao.ProtocolComponentRepository;
import cc.iotkit.dao.ProtocolConverterRepository;
import cc.iotkit.dao.UserInfoRepository;
import cc.iotkit.manager.service.DataOwnerService;
import cc.iotkit.manager.utils.AuthUtil;
import cc.iotkit.utils.AuthUtil;
import cc.iotkit.model.Paging;
import cc.iotkit.model.protocol.ProtocolComponent;
import cc.iotkit.model.protocol.ProtocolConverter;

View File

@ -4,7 +4,7 @@ import cc.iotkit.common.exception.BizException;
import cc.iotkit.common.utils.ReflectUtil;
import cc.iotkit.dao.*;
import cc.iotkit.manager.service.DataOwnerService;
import cc.iotkit.manager.utils.AuthUtil;
import cc.iotkit.utils.AuthUtil;
import cc.iotkit.model.Paging;
import cc.iotkit.model.rule.RuleInfo;
import cc.iotkit.model.rule.RuleLog;

View File

@ -4,7 +4,7 @@ import cc.iotkit.common.exception.BizException;
import cc.iotkit.dao.HomeRepository;
import cc.iotkit.dao.SpaceRepository;
import cc.iotkit.manager.service.DataOwnerService;
import cc.iotkit.manager.utils.AuthUtil;
import cc.iotkit.utils.AuthUtil;
import cc.iotkit.model.space.Home;
import cc.iotkit.model.space.Space;
import org.apache.commons.lang3.StringUtils;

View File

@ -6,7 +6,7 @@ import cc.iotkit.dao.*;
import cc.iotkit.manager.model.vo.FindDeviceVo;
import cc.iotkit.manager.model.vo.SpaceDeviceVo;
import cc.iotkit.manager.service.DataOwnerService;
import cc.iotkit.manager.utils.AuthUtil;
import cc.iotkit.utils.AuthUtil;
import cc.iotkit.model.UserInfo;
import cc.iotkit.model.device.DeviceInfo;
import cc.iotkit.model.product.Category;

View File

@ -8,11 +8,10 @@ import cc.iotkit.dao.AligenieDeviceRepository;
import cc.iotkit.dao.UserInfoRepository;
import cc.iotkit.manager.service.DataOwnerService;
import cc.iotkit.manager.service.PulsarAdminService;
import cc.iotkit.manager.utils.AuthUtil;
import cc.iotkit.utils.AuthUtil;
import cc.iotkit.model.UserInfo;
import cn.dev33.satoken.annotation.SaCheckRole;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import java.util.*;
@ -21,9 +20,6 @@ import java.util.*;
@RequestMapping("/user")
public class UserInfoController {
@Value("${app.systemRole}")
private String systemRole;
@Autowired
private UserInfoRepository userInfoRepository;
@Autowired
@ -55,7 +51,7 @@ public class UserInfoController {
user.setRoles(Collections.singletonList(Constants.ROLE_SYSTEM));
user.setPermissions(Collections.singletonList(Constants.PERMISSION_WRITE));
user.setCreateAt(System.currentTimeMillis());
user.setSecret(CodecUtil.aesEncrypt(Constants.PWD_SYSTEM_USER, Constants.PWD_SYSTEM_USER));
user.setSecret(AuthUtil.enCryptPwd(Constants.PWD_SYSTEM_USER));
userInfoRepository.save(user);
} catch (Throwable e) {
throw new BizException("add platform user error", e);
@ -79,7 +75,7 @@ public class UserInfoController {
user.setOwnerId(AuthUtil.getUserId());
user.setRoles(Collections.singletonList(Constants.ROLE_CLIENT));
user.setCreateAt(System.currentTimeMillis());
user.setSecret(CodecUtil.aesEncrypt(Constants.PWD_CLIENT_USER, Constants.ACCOUNT_SECRET));
user.setSecret(AuthUtil.enCryptPwd(Constants.PWD_CLIENT_USER));
userInfoRepository.save(user);
}

View File

@ -2,7 +2,7 @@ package cc.iotkit.manager.controller.aligenie;
import cc.iotkit.dao.AligenieProductRepository;
import cc.iotkit.manager.service.DataOwnerService;
import cc.iotkit.manager.utils.AuthUtil;
import cc.iotkit.utils.AuthUtil;
import cc.iotkit.model.aligenie.AligenieProduct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;

View File

@ -3,7 +3,7 @@ package cc.iotkit.manager.controller.api;
import cc.iotkit.dao.AppInfoRepository;
import cc.iotkit.dao.HomeRepository;
import cc.iotkit.dao.UserInfoRepository;
import cc.iotkit.manager.utils.AuthUtil;
import cc.iotkit.utils.AuthUtil;
import cc.iotkit.model.AppInfo;
import cc.iotkit.model.space.Home;
import cc.iotkit.model.UserInfo;

View File

@ -6,7 +6,7 @@ import cc.iotkit.dao.SpaceDeviceRepository;
import cc.iotkit.manager.model.vo.AppPageNode;
import cc.iotkit.manager.service.AppDesignService;
import cc.iotkit.manager.service.DeviceService;
import cc.iotkit.manager.utils.AuthUtil;
import cc.iotkit.utils.AuthUtil;
import cc.iotkit.model.device.DeviceInfo;
import cc.iotkit.model.space.SpaceDevice;
import io.swagger.annotations.ApiImplicitParam;

View File

@ -4,7 +4,7 @@ import cc.iotkit.dao.HomeRepository;
import cc.iotkit.dao.SpaceRepository;
import cc.iotkit.dao.UserActionLogRepository;
import cc.iotkit.dao.UserInfoRepository;
import cc.iotkit.manager.utils.AuthUtil;
import cc.iotkit.utils.AuthUtil;
import cc.iotkit.model.space.Home;
import cc.iotkit.model.space.Space;
import cc.iotkit.model.UserActionLog;

View File

@ -3,7 +3,7 @@ package cc.iotkit.manager.controller.api;
import cc.iotkit.dao.*;
import cc.iotkit.manager.model.vo.SpaceDeviceVo;
import cc.iotkit.manager.service.SpaceDeviceService;
import cc.iotkit.manager.utils.AuthUtil;
import cc.iotkit.utils.AuthUtil;
import cc.iotkit.model.device.DeviceInfo;
import cc.iotkit.model.product.Product;
import cc.iotkit.model.space.Space;

View File

@ -1,7 +1,7 @@
package cc.iotkit.manager.service;
import cc.iotkit.common.exception.BizException;
import cc.iotkit.manager.utils.AuthUtil;
import cc.iotkit.utils.AuthUtil;
import cc.iotkit.model.Owned;
import cc.iotkit.model.device.DeviceInfo;
import org.apache.commons.lang3.StringUtils;

View File

@ -8,32 +8,32 @@ spring:
max-file-size: 10MB
max-request-size: 12MB
#mongodb数据库配置
data:
mongodb:
uri: mongodb://填写mongodb地址/admin
uri: mongodb://填写mongodb地址
database: iotkit
elasticsearch:
rest:
uris: http://elasticsearch 连接地址
username: elasticsearch 用户名
password: 密码
#使用内置es的配置
uris: http://127.0.0.1:9200
username:
password:
connection-timeout: 10s
redis:
host: redis地址
#使用内置redis的配置
host: 127.0.0.1
port: 6379
database: 0
password: redis密码
password:
cache:
cache-names: foo,bar
caffeine:
spec: maximumSize=5000,expireAfterAccess=120s
mvc:
pathmatch:
matching-strategy: ant_path_matcher
#图片存储用的是阿里云oss如果需要上传产品图片才需要配置
aliyun:
bucketId: 填写阿里云bucketId
endpoint: oss-cn-shenzhen.aliyuncs.com
@ -42,7 +42,7 @@ aliyun:
sa-token:
# token名称 (同时也是cookie名称)
token-name: satoken
token-name: token
# token有效期单位s 默认30天, -1代表永不过期
timeout: 2592000
# token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒
@ -56,13 +56,11 @@ sa-token:
# 是否输出操作日志
is-log: false
#pulsar消息队列配置需要进行配置
pulsar:
broker: pulsar://pulsar broker地址:6650
service: http://pulsar 服务地址:8080
app:
systemRole: iot_system_user
mqtt:
url: tcp://填写mqtt连接地址
#认证中心地址
oauth2:
auth-server-url: http://127.0.0.1:8086

View File

@ -8,39 +8,41 @@ spring:
max-file-size: 10MB
max-request-size: 12MB
#mongodb数据库配置
data:
mongodb:
uri: mongodb://iotkit:123456@192.168.100.118:27017/?authSource=iotkit
uri: mongodb://填写mongodb地址
database: iotkit
elasticsearch:
rest:
uris: http://192.168.100.118:9200
#使用内置es的配置
uris: http://127.0.0.1:9200
username:
password:
connection-timeout: 10s
redis:
host: 192.168.100.50
#使用内置redis的配置
host: 127.0.0.1
port: 6379
database: 0
password: 5tgbNHY^
password:
cache:
cache-names: foo,bar
caffeine:
spec: maximumSize=5000,expireAfterAccess=300s
mvc:
pathmatch:
matching-strategy: ant_path_matcher
#图片存储用的是阿里云oss如果需要上传产品图片才需要配置
aliyun:
bucketId: iotkit-images
bucketId: 填写阿里云bucketId
endpoint: oss-cn-shenzhen.aliyuncs.com
accessKeyId: LTAI4FmGGouSnScYrGe7rQLA
accessKeySecret: 9S0xjffwmGW2B3HsE4Efw1TqUy8YXC
accessKeyId: 填写阿里云accessKeyId
accessKeySecret: 填写阿里云accessKeySecret
sa-token:
# token名称 (同时也是cookie名称)
token-name: satoken
token-name: token
# token有效期单位s 默认30天, -1代表永不过期
timeout: 2592000
# token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒
@ -54,23 +56,11 @@ sa-token:
# 是否输出操作日志
is-log: false
security:
oauth2:
resource:
filter-order: 3
oauth2:
auth-server-url: http://127.0.0.1:8086
#pulsar消息队列配置需要进行配置
pulsar:
broker: pulsar://192.168.100.66:6650
service: http://192.168.100.66:8080
app:
systemRole: iot_system_user
aligenie:
push:
device:
mqtt:
url: tcp:/192.168.100.118:1883
broker: pulsar://pulsar broker地址:6650
service: http://pulsar 服务地址:8080
#认证中心地址
oauth2:
auth-server-url: http://127.0.0.1:8086

View File

@ -48,6 +48,12 @@
<artifactId>dao</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -2,10 +2,12 @@ package cc.iotkit.oauth.controller;
import cc.iotkit.common.Constants;
import cc.iotkit.common.utils.CodecUtil;
import cc.iotkit.common.utils.ReflectUtil;
import cc.iotkit.dao.OauthClientCache;
import cc.iotkit.dao.UserInfoCache;
import cc.iotkit.model.OauthClient;
import cc.iotkit.model.UserInfo;
import cc.iotkit.oauth.vo.UserInfoVo;
import cc.iotkit.utils.SoMap;
import cn.dev33.satoken.stp.SaLoginConfig;
import cn.dev33.satoken.stp.StpUtil;
@ -63,15 +65,15 @@ public class AuthClientController {
.toString();
SoMap so = SoMap.getSoMap().setJsonString(str);
log.info("get token by code result:{}", so);
// code不等于200 代表请求失败
if (so.getInt("code") != 200) {
// 存在code,不是token结构
if (so.getInt("code") != 0) {
return SaResult.error(so.getString("msg"));
}
// 根据openid获取其对应的userId
SoMap data = so.getMap("data");
String uid = getUserIdByOpenid(data.getString("openid"));
String access_token = data.getString("access_token");
SoMap data = new SoMap();
String uid = getUserIdByOpenid(so.getString("openid"));
String access_token = so.getString("access_token");
UserInfo userInfo = userInfoCache.getUserInfo(uid);
data.put("name", userInfo.getNickName());
data.put("uid", uid);
@ -92,34 +94,17 @@ public class AuthClientController {
return new RedirectView(redirect_uri);
}
// 根据 Access-Token 置换相关的资源: 获取账号昵称、头像、性别等信息
@RequestMapping("/getUserinfo")
public SaResult getUserinfo(String accessToken) {
// 调用Server端接口查询开放的资源
String str = OkHttps.sync(serverUrl + "/oauth2/userinfo")
.addBodyPara("access_token", accessToken)
.post()
.getBody()
.toString();
SoMap so = SoMap.getSoMap().setJsonString(str);
// code不等于200 代表请求失败
if (so.getInt("code") != 200) {
return SaResult.error(so.getString("msg"));
}
// 返回相关参数 (data=获取到的资源 )
SoMap data = so.getMap("data");
return SaResult.data(data);
}
@GetMapping("/checkLogin")
public SaResult checkLogin() {
try {
StpUtil.checkLogin();
String uid = StpUtil.getLoginId().toString();
UserInfo userInfo = userInfoCache.getUserInfo(uid);
UserInfoVo userVo = new UserInfoVo();
ReflectUtil.copyNoNulls(userInfo, userVo);
return SaResult.ok().setData(userVo);
} catch (Throwable e) {
return SaResult.error("no login");
}
return SaResult.ok();
}
@SneakyThrows

View File

@ -1,25 +1,28 @@
package cc.iotkit.oauth.controller;
import cc.iotkit.common.Constants;
import cc.iotkit.common.utils.CodecUtil;
import cc.iotkit.common.utils.JsonUtil;
import cc.iotkit.dao.UserInfoRepository;
import cc.iotkit.model.UserInfo;
import cc.iotkit.oauth.service.TokenRequestHandler;
import cc.iotkit.utils.AuthUtil;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.oauth2.config.SaOAuth2Config;
import cn.dev33.satoken.oauth2.logic.SaOAuth2Util;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
@Slf4j
@RestController
public class AuthServerController {
@ -28,8 +31,10 @@ public class AuthServerController {
// 处理所有OAuth相关请求
@RequestMapping("/oauth2/*")
public Object request() {
return TokenRequestHandler.serverRequest();
public Object request(HttpServletRequest request) {
Object result = TokenRequestHandler.serverRequest();
log.info("oauth path:{},result:{}", request.getRequestURI(), JsonUtil.toJsonString(result));
return result;
}
// Sa-OAuth2 定制化配置
@ -44,8 +49,7 @@ public class AuthServerController {
UserInfo userInfo = userInfoRepository.findByUid(name);
if (userInfo != null) {
String secret = userInfo.getSecret();
String encodePwd = CodecUtil.aesEncrypt(pwd, Constants.ACCOUNT_SECRET);
if (encodePwd.equals(secret)) {
if (AuthUtil.checkPwd(pwd, secret)) {
StpUtil.login(userInfo.getId(), "PC");
return SaResult.ok();
}
@ -94,4 +98,6 @@ public class AuthServerController {
map.put("address", "山东省 青岛市 城阳区");
return SaResult.data(map);
}
}

View File

@ -6,7 +6,6 @@ import cn.dev33.satoken.stp.StpInterface;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component
@ -20,10 +19,8 @@ public class StpInterfaceImpl implements StpInterface {
*/
@Override
public List<String> getPermissionList(Object loginId, String loginType) {
// 本list仅做模拟实际项目中要根据具体业务逻辑来查询权限
List<String> list = new ArrayList<String>();
list.add("write");
return list;
UserInfo userInfo = userInfoCache.getUserInfo(loginId.toString());
return userInfo.getPermissions();
}
/**

View File

@ -8,7 +8,13 @@ import cn.dev33.satoken.oauth2.config.SaOAuth2Config;
import cn.dev33.satoken.oauth2.exception.SaOAuth2Exception;
import cn.dev33.satoken.oauth2.logic.SaOAuth2Consts;
import cn.dev33.satoken.oauth2.logic.SaOAuth2Handle;
import cn.dev33.satoken.oauth2.logic.SaOAuth2Util;
import cn.dev33.satoken.oauth2.model.AccessTokenModel;
import cn.dev33.satoken.oauth2.model.ClientTokenModel;
import cn.dev33.satoken.oauth2.model.RequestAuthModel;
import cn.dev33.satoken.oauth2.model.SaClientModel;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
public class TokenRequestHandler {
@ -25,9 +31,9 @@ public class TokenRequestHandler {
return SaOAuth2Handle.authorize(req, res, cfg);
}
} else if (req.isPath(SaOAuth2Consts.Api.token) && req.isParam(SaOAuth2Consts.Param.grant_type, SaOAuth2Consts.GrantType.authorization_code)) {
return SaOAuth2Handle.token(req, res, cfg);
return token(req, res, cfg);
} else if (req.isPath(SaOAuth2Consts.Api.token) && req.isParam(SaOAuth2Consts.Param.grant_type, SaOAuth2Consts.GrantType.refresh_token)) {
return SaOAuth2Handle.refreshToken(req);
return refreshToken(req);
} else if (req.isPath(SaOAuth2Consts.Api.revoke)) {
return SaOAuth2Handle.revokeToken(req);
} else if (req.isPath(SaOAuth2Consts.Api.doLogin)) {
@ -46,17 +52,65 @@ public class TokenRequestHandler {
if (!cfg.getIsPassword() || !cm.isPassword && !cm.isAutoMode) {
throw new SaOAuth2Exception("暂未开放的授权模式");
} else {
return SaOAuth2Handle.password(req, res, cfg);
return password(req, res, cfg);
}
} else if (req.isPath(SaOAuth2Consts.Api.token) && req.isParam(SaOAuth2Consts.Param.grant_type, SaOAuth2Consts.GrantType.client_credentials)) {
cm = SaOAuth2Handle.currClientModel();
if (!cfg.getIsClient() || !cm.isClient && !cm.isAutoMode) {
throw new SaOAuth2Exception("暂未开放的授权模式");
} else {
return SaOAuth2Handle.clientToken(req, res, cfg);
return clientToken(req, res, cfg);
}
} else {
return "{\"msg\": \"not handle\"}";
}
}
public static Object token(SaRequest req, SaResponse res, SaOAuth2Config cfg) {
String code = req.getParamNotNull(SaOAuth2Consts.Param.code);
String clientId = req.getParamNotNull(SaOAuth2Consts.Param.client_id);
String clientSecret = req.getParamNotNull(SaOAuth2Consts.Param.client_secret);
String redirectUri = req.getParam(SaOAuth2Consts.Param.redirect_uri);
SaOAuth2Util.checkGainTokenParam(code, clientId, clientSecret, redirectUri);
AccessTokenModel token = SaOAuth2Util.generateAccessToken(code);
return token.toLineMap();
}
public static Object refreshToken(SaRequest req) {
String clientId = req.getParamNotNull(SaOAuth2Consts.Param.client_id);
String clientSecret = req.getParamNotNull(SaOAuth2Consts.Param.client_secret);
String refreshToken = req.getParamNotNull(SaOAuth2Consts.Param.refresh_token);
SaOAuth2Util.checkRefreshTokenParam(clientId, clientSecret, refreshToken);
return SaOAuth2Util.refreshAccessToken(refreshToken).toLineMap();
}
public static Object password(SaRequest req, SaResponse res, SaOAuth2Config cfg) {
String username = req.getParamNotNull(SaOAuth2Consts.Param.username);
String password = req.getParamNotNull(SaOAuth2Consts.Param.password);
String clientId = req.getParamNotNull(SaOAuth2Consts.Param.client_id);
String scope = req.getParam(SaOAuth2Consts.Param.scope, "");
SaOAuth2Util.checkContract(clientId, scope);
SaHolder.getStorage().set(StpUtil.stpLogic.splicingKeyJustCreatedSave(), "no-token");
Object retObj = cfg.getDoLoginHandle().apply(username, password);
if (!StpUtil.isLogin()) {
return retObj;
} else {
RequestAuthModel ra = new RequestAuthModel();
ra.clientId = clientId;
ra.loginId = StpUtil.getLoginId();
ra.scope = scope;
AccessTokenModel at = SaOAuth2Util.generateAccessToken(ra, true);
return at.toLineMap();
}
}
public static Object clientToken(SaRequest req, SaResponse res, SaOAuth2Config cfg) {
String clientId = req.getParamNotNull(SaOAuth2Consts.Param.client_id);
String clientSecret = req.getParamNotNull(SaOAuth2Consts.Param.client_secret);
String scope = req.getParam(SaOAuth2Consts.Param.scope);
SaOAuth2Util.checkContract(clientId, scope);
SaOAuth2Util.checkClientSecret(clientId, clientSecret);
ClientTokenModel ct = SaOAuth2Util.generateClientToken(clientId, scope);
return ct.toLineMap();
}
}

View File

@ -0,0 +1,70 @@
package cc.iotkit.oauth.vo;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class UserInfoVo {
/**
*
*/
private String uid;
/**
*
*/
private String nickName;
/**
* 0- 1-male,2-female
*/
private Integer gender;
/**
*
*/
private String avatarUrl;
private String email;
private String address;
/**
* Id
*/
private String currHomeId;
/**
*
* 0:
* 1:
*/
private Integer type;
/**
*
*/
private List<String> roles = new ArrayList<>();
/**
*
*/
private List<String> permissions = new ArrayList<>();
/**
* 使
* :Constants.THIRD_PLATFORM
*/
private List<String> usePlatforms = new ArrayList<>();
}

View File

@ -1,9 +1,10 @@
package cc.iotkit.manager.utils;
package cc.iotkit.utils;
import cc.iotkit.common.Constants;
import cc.iotkit.common.utils.CodecUtil;
import cn.dev33.satoken.stp.StpUtil;
import org.apache.commons.lang3.RandomUtils;
import java.util.ArrayList;
import java.util.List;
public class AuthUtil {
@ -28,4 +29,14 @@ public class AuthUtil {
return AuthUtil.getUserRoles().contains(Constants.ROLE_WRITE);
}
public static String enCryptPwd(String pwd) throws Exception {
return CodecUtil.aesEncrypt(CodecUtil.md5Str(pwd) + ":"
+ RandomUtils.nextInt(1000, 9999), Constants.ACCOUNT_SECRET);
}
public static boolean checkPwd(String pwd, String secret) throws Exception {
String code = CodecUtil.aesDecrypt(secret, Constants.ACCOUNT_SECRET);
String[] arr = code.split(":");
return arr.length > 0 && CodecUtil.md5Str(pwd).equals(arr[0]);
}
}

View File

@ -0,0 +1,14 @@
import cc.iotkit.utils.AuthUtil;
import org.junit.Test;
public class GenPwdSecret {
@Test
public void gen() throws Exception {
//生成密码加密内容
String secret = AuthUtil.enCryptPwd("c123456");
System.out.println(secret);
System.out.println(AuthUtil.checkPwd("c123456", secret));
}
}

View File

@ -125,7 +125,7 @@ public class ApiTool {
request = request
.timeout(timeout)
.putHeader("wrap-response", "json")
.putHeader("authorization", "Bearer " + token);
.putHeader("token", token);
AtomicReference<ApiResponse> apiResponse = new AtomicReference<>(
new ApiResponse(500, "", null, System.currentTimeMillis()));

View File

View File

View File