diff --git a/sec-api/src/main/java/com/imdroid/secapi/dto/ApiKey.java b/sec-api/src/main/java/com/imdroid/secapi/dto/ApiKey.java new file mode 100644 index 00000000..46a58a4d --- /dev/null +++ b/sec-api/src/main/java/com/imdroid/secapi/dto/ApiKey.java @@ -0,0 +1,22 @@ +package com.imdroid.secapi.dto; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@TableName(value = "ApiKey") +public class ApiKey { + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + Integer tenantid; + String tenantname; + private String apikey; + private String secret; +} diff --git a/sec-api/src/main/java/com/imdroid/secapi/dto/ApiKeyMapper.java b/sec-api/src/main/java/com/imdroid/secapi/dto/ApiKeyMapper.java new file mode 100644 index 00000000..7e8677ca --- /dev/null +++ b/sec-api/src/main/java/com/imdroid/secapi/dto/ApiKeyMapper.java @@ -0,0 +1,10 @@ +package com.imdroid.secapi.dto; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + + +@Mapper +public interface ApiKeyMapper extends BaseMapper { + +} diff --git a/sec-beidou/src/main/java/com/imdroid/beidou/controller/ApiKeyController.java b/sec-beidou/src/main/java/com/imdroid/beidou/controller/ApiKeyController.java new file mode 100644 index 00000000..0984f2ee --- /dev/null +++ b/sec-beidou/src/main/java/com/imdroid/beidou/controller/ApiKeyController.java @@ -0,0 +1,104 @@ +package com.imdroid.beidou.controller; + +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.imdroid.beidou.common.HttpResult; +import com.imdroid.common.util.AppUtils; +import com.imdroid.secapi.dto.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpSession; +import java.util.List; + +@Controller +public class ApiKeyController extends BasicController { + @Autowired + ApiKeyMapper apiKeyMapper; + @Autowired + TenantMapper tenantMapper; + @Autowired + OpLogManager opLogManager; + + /**** 推送页面 *****/ + @RequestMapping("/sys/apikey") + public String listApiKey(Model m, HttpSession session) { + initModel(m, session); + return "/page/api_key"; + } + + @RequestMapping("/sys/apikey/add") + public String addApiKey(Model m, HttpSession session){ + initModel(m, session); + QueryWrapper queryWrapper = new QueryWrapper<>(); + Integer tenantId = getTenantId(session); + if (tenantId != null && tenantId != Tenant.SAAS_PROVIDER_ID) { + queryWrapper.eq("id", tenantId); + } + List tenants = tenantMapper.selectList(queryWrapper); + m.addAttribute("tenant_list", tenants); + + String apiKey = AppUtils.getAppId(); + String apiSecret = AppUtils.getAppSecret(apiKey); + m.addAttribute("api_key", apiKey); + m.addAttribute("api_secret", apiSecret); + + return "/page/table/api_key_add"; + } + + /**** 推送数据 *****/ + @RequestMapping("/sys/apikey/list") + @ResponseBody + public JSONObject list(HttpSession session, int page, int limit) { + Page pageable = new Page<>(page, limit); + QueryWrapper queryWrapper = new QueryWrapper<>(); + Integer tenantId = getTenantId(session); + if (tenantId != null && tenantId != Tenant.SAAS_PROVIDER_ID) { + queryWrapper.eq("tenantid", tenantId); + } + IPage cs = apiKeyMapper.selectPage(pageable, queryWrapper); + + JSONObject jsonObject = new JSONObject(); + jsonObject.put("code", 0); + jsonObject.put("msg", ""); + jsonObject.put("count", cs.getTotal()); + jsonObject.put("data", cs.getRecords()); + return jsonObject; + } + + @PostMapping("/sys/apikey/update") + @ResponseBody + public String update(HttpSession session, @RequestBody JSONObject object) throws Exception { + // 从请求参数中创建对象 + ApiKey apiKey = JSONObject.toJavaObject(object,ApiKey.class); + Tenant tenant = tenantMapper.selectById(apiKey.getTenantid()); + int num = 0; + if(tenant!=null) { + apiKey.setTenantname(tenant.getName()); + num = apiKeyMapper.insert(apiKey); + } + if (num == 0) { + return HttpResult.failed(); + } else { + return HttpResult.ok(); + } + } + + @PostMapping("/sys/apikey/delete") + @ResponseBody + public String delete(HttpSession session, @RequestParam Integer id) throws Exception { + int num = apiKeyMapper.deleteById(id); + opLogManager.addLog(getLoginUser(session),getTenantId(session), + OpLogManager.OP_TYPE_DEL, + OpLogManager.OP_OBJ_SYS, + "api key deleted"); + if (num == 0) { + return HttpResult.failed(); + } else return HttpResult.ok(); + } + +} diff --git a/sec-beidou/src/main/resources/db/schema.sql b/sec-beidou/src/main/resources/db/schema.sql index ccf72fcb..645f6136 100644 --- a/sec-beidou/src/main/resources/db/schema.sql +++ b/sec-beidou/src/main/resources/db/schema.sql @@ -302,4 +302,13 @@ CREATE TABLE IF NOT EXISTS `OperationLog` ( `op_object` smallint DEFAULT NULL, `content` varchar(256) DEFAULT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; \ No newline at end of file +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE IF NOT EXISTS `ApiKey` ( + `id` bigint(20) AUTO_INCREMENT, + `tenantid` int DEFAULT 0, + `apikey` varchar(64) NOT NULL, + `secret` varchar(256) NOT NULL, + `tenantname` varchar(100) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/sec-beidou/src/main/resources/static/api/init_super_admin.json b/sec-beidou/src/main/resources/static/api/init_super_admin.json index 5f56cb00..5229f44c 100644 --- a/sec-beidou/src/main/resources/static/api/init_super_admin.json +++ b/sec-beidou/src/main/resources/static/api/init_super_admin.json @@ -162,6 +162,12 @@ "href": "sys/oplog", "icon": "fa fa-book", "target": "_self" + }, + { + "title": "API", + "href": "sys/apikey", + "icon": "fa fa-key", + "target": "_self" } ] } diff --git a/sec-beidou/src/main/resources/templates/page/api_key.html b/sec-beidou/src/main/resources/templates/page/api_key.html new file mode 100644 index 00000000..c868a97c --- /dev/null +++ b/sec-beidou/src/main/resources/templates/page/api_key.html @@ -0,0 +1,110 @@ + + + + + Api Key + + + + + + + +
+
+ +
+ + + + + +
+
+ + + + + \ No newline at end of file diff --git a/sec-beidou/src/main/resources/templates/page/table/api_key_add.html b/sec-beidou/src/main/resources/templates/page/table/api_key_add.html new file mode 100644 index 00000000..7b252c7d --- /dev/null +++ b/sec-beidou/src/main/resources/templates/page/table/api_key_add.html @@ -0,0 +1,82 @@ + + + + + API Key + + + + + + + + + +
+ +
+ +
+ +
+
+
+ +
+ +
+
+
+
+ +
+ +
+
+
+ +
+
+ +
+
+
+ + + + + + \ No newline at end of file diff --git a/sec-beidou/src/main/resources/templates/page/table/user_add.html b/sec-beidou/src/main/resources/templates/page/table/user_add.html index 4880c820..db1c9ed3 100644 --- a/sec-beidou/src/main/resources/templates/page/table/user_add.html +++ b/sec-beidou/src/main/resources/templates/page/table/user_add.html @@ -2,7 +2,7 @@ - 组织 + 用户 diff --git a/sec-common/src/main/java/com/imdroid/common/util/AppUtils.java b/sec-common/src/main/java/com/imdroid/common/util/AppUtils.java new file mode 100644 index 00000000..817c2f1e --- /dev/null +++ b/sec-common/src/main/java/com/imdroid/common/util/AppUtils.java @@ -0,0 +1,76 @@ +package com.imdroid.common.util; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; +import java.util.UUID; + +public class AppUtils { + //生成 app_secret 密钥 + private final static String SERVER_NAME = "mazhq_abc123"; + private final static String[] chars = new String[]{"a", "b", "c", "d", "e", "f", + "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", + "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", + "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", + "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", + "W", "X", "Y", "Z"}; + + /** + * @Description:

+ * 短8位UUID思想其实借鉴微博短域名的生成方式,但是其重复概率过高,而且每次生成4个,需要随即选取一个。 + * 本算法利用62个可打印字符,通过随机生成32位UUID,由于UUID都为十六进制,所以将UUID分成8组,每4个为一组,然后通过模62操作,结果作为索引取出字符, + * 这样重复率大大降低。 + * 经测试,在生成一千万个数据也没有出现重复,完全满足大部分需求。 + *

+ * @author mazhq + * @date 2019/8/27 16:16 + */ + public static String getAppId() { + StringBuffer shortBuffer = new StringBuffer(); + String uuid = UUID.randomUUID().toString().replace("-", ""); + for (int i = 0; i < 8; i++) { + String str = uuid.substring(i * 4, i * 4 + 4); + int x = Integer.parseInt(str, 16); + shortBuffer.append(chars[x % 0x3E]); + } + return shortBuffer.toString(); + + } + + /** + *

+ * 通过appId和内置关键词生成APP Secret + *

+ * @author mazhq + * @date 2019/8/27 16:32 + */ + public static String getAppSecret(String appId) { + try { + String[] array = new String[]{appId, SERVER_NAME}; + StringBuffer sb = new StringBuffer(); + // 字符串排序 + Arrays.sort(array); + for (int i = 0; i < array.length; i++) { + sb.append(array[i]); + } + String str = sb.toString(); + MessageDigest md = MessageDigest.getInstance("SHA-1"); + md.update(str.getBytes()); + byte[] digest = md.digest(); + + StringBuffer hexstr = new StringBuffer(); + String shaHex = ""; + for (int i = 0; i < digest.length; i++) { + shaHex = Integer.toHexString(digest[i] & 0xFF); + if (shaHex.length() < 2) { + hexstr.append(0); + } + hexstr.append(shaHex); + } + return hexstr.toString(); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + throw new RuntimeException(); + } + } +} diff --git a/sec-exapi/src/main/java/com/imdroid/sec_exapi/auth/JwtInterceptor.java b/sec-exapi/src/main/java/com/imdroid/sec_exapi/auth/JwtInterceptor.java index 4d987ffc..72e85c2a 100644 --- a/sec-exapi/src/main/java/com/imdroid/sec_exapi/auth/JwtInterceptor.java +++ b/sec-exapi/src/main/java/com/imdroid/sec_exapi/auth/JwtInterceptor.java @@ -7,8 +7,8 @@ import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.exceptions.JWTDecodeException; import com.auth0.jwt.exceptions.JWTVerificationException; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; -import com.imdroid.secapi.dto.User; -import com.imdroid.secapi.dto.UserMapper; +import com.imdroid.secapi.dto.ApiKey; +import com.imdroid.secapi.dto.ApiKeyMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.method.HandlerMethod; @@ -20,7 +20,7 @@ import javax.servlet.http.HttpServletResponse; @Component public class JwtInterceptor implements HandlerInterceptor { @Autowired - private UserMapper userMapper; + private ApiKeyMapper apiMapper; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String token = request.getHeader("Authorization"); @@ -34,23 +34,23 @@ public class JwtInterceptor implements HandlerInterceptor { return false; } // 获取 token 中的userId - String userId; + String apiKey; try { - userId = JWT.decode(token).getAudience().get(0); + apiKey = JWT.decode(token).getAudience().get(0); } catch (JWTDecodeException j) { response.setStatus(401); return false; } //根据token中的userid查询数据库 - QueryWrapper queryWrapper = new QueryWrapper<>(); - queryWrapper.eq("name",userId); - User user = userMapper.selectOne(queryWrapper); - if (user == null) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("apikey",apiKey); + ApiKey api = apiMapper.selectOne(queryWrapper); + if (api == null) { response.setStatus(401); return false; } // 用户密码加签验证 token - JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build(); + JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(api.getSecret())).build(); try{ jwtVerifier.verify(token); // 验证token } catch (JWTVerificationException e) { @@ -58,7 +58,7 @@ public class JwtInterceptor implements HandlerInterceptor { return false; } - request.setAttribute("user",user); + request.setAttribute("tenantId",api.getTenantid()); return true; } } \ No newline at end of file diff --git a/sec-exapi/src/main/java/com/imdroid/sec_exapi/controller/DeviceApi.java b/sec-exapi/src/main/java/com/imdroid/sec_exapi/controller/DeviceApi.java index a328ce4b..9db33490 100644 --- a/sec-exapi/src/main/java/com/imdroid/sec_exapi/controller/DeviceApi.java +++ b/sec-exapi/src/main/java/com/imdroid/sec_exapi/controller/DeviceApi.java @@ -24,9 +24,9 @@ public class DeviceApi { @GetMapping(value = "/gnss/device" ) public String query(HttpServletRequest request, String projectName) { - User user = (User) request.getAttribute("user"); + Integer tenantId = (Integer) request.getAttribute("tenantId"); - List deviceList = deviceMapper.queryDeployedByProject(user.getTenantid(),projectName); + List deviceList = deviceMapper.queryDeployedByProject(tenantId,projectName); DeviceQueryResponse deviceQueryResponse = new DeviceQueryResponse(); deviceQueryResponse.OK(); for(GnssStatusJoin device:deviceList){ diff --git a/sec-exapi/src/main/java/com/imdroid/sec_exapi/controller/GnssDataApi.java b/sec-exapi/src/main/java/com/imdroid/sec_exapi/controller/GnssDataApi.java index f9cbcd80..5954ff49 100644 --- a/sec-exapi/src/main/java/com/imdroid/sec_exapi/controller/GnssDataApi.java +++ b/sec-exapi/src/main/java/com/imdroid/sec_exapi/controller/GnssDataApi.java @@ -38,26 +38,26 @@ public class GnssDataApi { queryWrapper.isNotNull("rpose"); if(!StrUtil.isBlank(beginDate)){ - queryWrapper.ge("createtime",beginDate); + queryWrapper.ge("t.createtime",beginDate); } if(!StrUtil.isBlank(endDate)){ - queryWrapper.ge("createtime",endDate); + queryWrapper.ge("t.createtime",endDate); } if(StrUtil.isBlank(sortType) || sortType.equals("asc")){ - queryWrapper.orderByAsc("createtime"); + queryWrapper.orderByAsc("t.createtime"); } else{ - queryWrapper.orderByDesc("createtime"); + queryWrapper.orderByDesc("t.createtime"); } if(pageSize!=null){ - if(pageSize > 10000) pageSize=10000; + if(pageSize > 1000) pageSize=1000; queryWrapper.last("limit "+pageSize); } else{ - queryWrapper.last("limit 500"); + queryWrapper.last("limit 100"); } List dataList = dataMapper.selectList(queryWrapper); @@ -73,7 +73,7 @@ public class GnssDataApi { gnssData.setCreateTime(data.getCreatetime().format(formatter)); gnssData.setRpose(data.getRpose()); gnssData.setRposn(data.getRposn()); - gnssData.setRposd(data.getRposd()); + gnssData.setRposu(data.getRposd()); gnssResponse.getData().add(gnssData); lastRecordTime = recordTime.plusMinutes(30); } diff --git a/sec-exapi/src/main/java/com/imdroid/sec_exapi/controller/UserApi.java b/sec-exapi/src/main/java/com/imdroid/sec_exapi/controller/UserApi.java index 26434a8c..d6b1beb2 100644 --- a/sec-exapi/src/main/java/com/imdroid/sec_exapi/controller/UserApi.java +++ b/sec-exapi/src/main/java/com/imdroid/sec_exapi/controller/UserApi.java @@ -1,7 +1,6 @@ package com.imdroid.sec_exapi.controller; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; -import com.imdroid.common.util.BCryptPasswordEncoderUtil; import com.imdroid.common.util.GsonUtil; import com.imdroid.sec_exapi.auth.TokenUtils; import com.imdroid.sec_exapi.entity.LoginResponse; @@ -21,34 +20,35 @@ public class UserApi { @Autowired private TenantMapper tenantMapper; @Autowired - UserMapper userMapper; + ApiKeyMapper apiMapper; @Autowired OpLogManager opLogManager; @Autowired GnssDeviceMapper deviceMapper; - private final BCryptPasswordEncoderUtil bCryptPasswordEncoderUtil=new BCryptPasswordEncoderUtil(); final Logger logger = LoggerFactory.getLogger(UserApi.class); // 登录 @PostMapping(value = "/login" ) public String login(String apiKey, String apiSecret, HttpServletResponse response) { - User user = userMapper.queryByName(apiKey); + QueryWrapper queryWrapper1 = new QueryWrapper<>(); + queryWrapper1.eq("apikey", apiKey); + ApiKey api = apiMapper.selectOne(queryWrapper1); LoginResponse loginResponse = new LoginResponse(); - if (user == null || !bCryptPasswordEncoderUtil.matches(apiSecret, user.getPassword())) { + if (api == null || !api.getSecret().equals(apiSecret)) { loginResponse.failed(); } else { - opLogManager.addLog(user.getName(), user.getTenantid(), + opLogManager.addLog(api.getApikey(), api.getTenantid(), OpLogManager.OP_TYPE_LOGIN, OpLogManager.OP_OBJ_USER, "api login"); loginResponse.OK(); - String token = TokenUtils.getToken(apiKey, user.getPassword()); + String token = TokenUtils.getToken(apiKey, apiSecret); loginResponse.setToken(token); //查询项目 - Tenant tenant = tenantMapper.selectById(user.getTenantid()); + Tenant tenant = tenantMapper.selectById(api.getTenantid()); QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("tenantid", tenant.getId()); queryWrapper.eq("opmode", GnssDevice.OP_MODE_USE);