提交 ced8c21e authored 作者: fit2cloud-chenyw's avatar fit2cloud-chenyw

feat: 公共链接分享

上级 89f0c5e7
...@@ -10,8 +10,15 @@ public class RsaProperties { ...@@ -10,8 +10,15 @@ public class RsaProperties {
public static String privateKey; public static String privateKey;
public static String publicKey;
@Value("${rsa.private_key}") @Value("${rsa.private_key}")
public void setPrivateKey(String privateKey) { public void setPrivateKey(String privateKey) {
RsaProperties.privateKey = privateKey; RsaProperties.privateKey = privateKey;
} }
@Value("${rsa.public_key}")
public void setPublicKey(String publicKey) {
RsaProperties.publicKey = publicKey;
}
} }
...@@ -118,6 +118,24 @@ public class JWTUtils { ...@@ -118,6 +118,24 @@ public class JWTUtils {
} }
} }
public static String signLink(String resourceId, String secret) {
Algorithm algorithm = Algorithm.HMAC256(secret);
return JWT.create().withClaim("resourceId", resourceId).sign(algorithm);
}
public static boolean verifyLink(String token,String resourceId, String secret) {
Algorithm algorithm = Algorithm.HMAC256(secret);
JWTVerifier verifier = JWT.require(algorithm)
.withClaim("resourceId", resourceId)
.build();
try {
verifier.verify(token);
return true;
}catch (Exception e){
return false;
}
}
/** /**
* 获取当前token上次操作时间 * 获取当前token上次操作时间
* @param token * @param token
......
package io.dataease.auth.util; package io.dataease.auth.util;
import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.ArrayUtils;
import javax.crypto.Cipher; import javax.crypto.Cipher;
import java.security.KeyFactory; import java.security.KeyFactory;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
public class RsaUtil { public class RsaUtil {
...@@ -23,7 +26,39 @@ public class RsaUtil { ...@@ -23,7 +26,39 @@ public class RsaUtil {
PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec5); PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec5);
Cipher cipher = Cipher.getInstance("RSA"); Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey); cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] result = cipher.doFinal(Base64.decodeBase64(text)); // byte[] result = cipher.doFinal(Base64.decodeBase64(text));
// 下面该用分段加密
byte[] result = null;
byte[] b = Base64.decodeBase64(text);
for (int i = 0; i < b.length; i += 64) {
byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(b, i,i + 64));
result = ArrayUtils.addAll(result, doFinal);
}
return new String(result); return new String(result);
} }
/**
* 公钥加密
*
* @param publicKeyText 公钥
* @param text 待加密的文本
* @return /
*/
public static String encryptByPublicKey(String publicKeyText, String text) throws Exception {
X509EncodedKeySpec x509EncodedKeySpec2 = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyText));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec2);
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
/*byte[] result = cipher.doFinal(text.getBytes());*/
// 下面该用分段加密
byte[] result = null;
byte[] b = text.getBytes("utf-8");
for (int i = 0; i < b.length; i += 50) {
byte[] doFinal = cipher.doFinal(ArrayUtils.subarray(b, i,i + 50));
result = ArrayUtils.addAll(result, doFinal);
}
return Base64.encodeBase64String(result);
}
} }
...@@ -7,6 +7,8 @@ import lombok.Data; ...@@ -7,6 +7,8 @@ import lombok.Data;
public class PanelLink implements Serializable { public class PanelLink implements Serializable {
private String resourceId; private String resourceId;
private Boolean valid;
private Boolean enablePwd; private Boolean enablePwd;
private String pwd; private String pwd;
......
...@@ -174,6 +174,66 @@ public class PanelLinkExample { ...@@ -174,6 +174,66 @@ public class PanelLinkExample {
return (Criteria) this; return (Criteria) this;
} }
public Criteria andValidIsNull() {
addCriterion("`valid` is null");
return (Criteria) this;
}
public Criteria andValidIsNotNull() {
addCriterion("`valid` is not null");
return (Criteria) this;
}
public Criteria andValidEqualTo(Boolean value) {
addCriterion("`valid` =", value, "valid");
return (Criteria) this;
}
public Criteria andValidNotEqualTo(Boolean value) {
addCriterion("`valid` <>", value, "valid");
return (Criteria) this;
}
public Criteria andValidGreaterThan(Boolean value) {
addCriterion("`valid` >", value, "valid");
return (Criteria) this;
}
public Criteria andValidGreaterThanOrEqualTo(Boolean value) {
addCriterion("`valid` >=", value, "valid");
return (Criteria) this;
}
public Criteria andValidLessThan(Boolean value) {
addCriterion("`valid` <", value, "valid");
return (Criteria) this;
}
public Criteria andValidLessThanOrEqualTo(Boolean value) {
addCriterion("`valid` <=", value, "valid");
return (Criteria) this;
}
public Criteria andValidIn(List<Boolean> values) {
addCriterion("`valid` in", values, "valid");
return (Criteria) this;
}
public Criteria andValidNotIn(List<Boolean> values) {
addCriterion("`valid` not in", values, "valid");
return (Criteria) this;
}
public Criteria andValidBetween(Boolean value1, Boolean value2) {
addCriterion("`valid` between", value1, value2, "valid");
return (Criteria) this;
}
public Criteria andValidNotBetween(Boolean value1, Boolean value2) {
addCriterion("`valid` not between", value1, value2, "valid");
return (Criteria) this;
}
public Criteria andEnablePwdIsNull() { public Criteria andEnablePwdIsNull() {
addCriterion("enable_pwd is null"); addCriterion("enable_pwd is null");
return (Criteria) this; return (Criteria) this;
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
<mapper namespace="io.dataease.base.mapper.PanelLinkMapper"> <mapper namespace="io.dataease.base.mapper.PanelLinkMapper">
<resultMap id="BaseResultMap" type="io.dataease.base.domain.PanelLink"> <resultMap id="BaseResultMap" type="io.dataease.base.domain.PanelLink">
<id column="resource_id" jdbcType="VARCHAR" property="resourceId" /> <id column="resource_id" jdbcType="VARCHAR" property="resourceId" />
<result column="valid" jdbcType="BIT" property="valid" />
<result column="enable_pwd" jdbcType="BIT" property="enablePwd" /> <result column="enable_pwd" jdbcType="BIT" property="enablePwd" />
<result column="pwd" jdbcType="VARCHAR" property="pwd" /> <result column="pwd" jdbcType="VARCHAR" property="pwd" />
</resultMap> </resultMap>
...@@ -65,7 +66,7 @@ ...@@ -65,7 +66,7 @@
</where> </where>
</sql> </sql>
<sql id="Base_Column_List"> <sql id="Base_Column_List">
resource_id, enable_pwd, pwd resource_id, `valid`, enable_pwd, pwd
</sql> </sql>
<select id="selectByExample" parameterType="io.dataease.base.domain.PanelLinkExample" resultMap="BaseResultMap"> <select id="selectByExample" parameterType="io.dataease.base.domain.PanelLinkExample" resultMap="BaseResultMap">
select select
...@@ -98,10 +99,10 @@ ...@@ -98,10 +99,10 @@
</if> </if>
</delete> </delete>
<insert id="insert" parameterType="io.dataease.base.domain.PanelLink"> <insert id="insert" parameterType="io.dataease.base.domain.PanelLink">
insert into panel_link (resource_id, enable_pwd, pwd insert into panel_link (resource_id, `valid`, enable_pwd,
) pwd)
values (#{resourceId,jdbcType=VARCHAR}, #{enablePwd,jdbcType=BIT}, #{pwd,jdbcType=VARCHAR} values (#{resourceId,jdbcType=VARCHAR}, #{valid,jdbcType=BIT}, #{enablePwd,jdbcType=BIT},
) #{pwd,jdbcType=VARCHAR})
</insert> </insert>
<insert id="insertSelective" parameterType="io.dataease.base.domain.PanelLink"> <insert id="insertSelective" parameterType="io.dataease.base.domain.PanelLink">
insert into panel_link insert into panel_link
...@@ -109,6 +110,9 @@ ...@@ -109,6 +110,9 @@
<if test="resourceId != null"> <if test="resourceId != null">
resource_id, resource_id,
</if> </if>
<if test="valid != null">
`valid`,
</if>
<if test="enablePwd != null"> <if test="enablePwd != null">
enable_pwd, enable_pwd,
</if> </if>
...@@ -120,6 +124,9 @@ ...@@ -120,6 +124,9 @@
<if test="resourceId != null"> <if test="resourceId != null">
#{resourceId,jdbcType=VARCHAR}, #{resourceId,jdbcType=VARCHAR},
</if> </if>
<if test="valid != null">
#{valid,jdbcType=BIT},
</if>
<if test="enablePwd != null"> <if test="enablePwd != null">
#{enablePwd,jdbcType=BIT}, #{enablePwd,jdbcType=BIT},
</if> </if>
...@@ -140,6 +147,9 @@ ...@@ -140,6 +147,9 @@
<if test="record.resourceId != null"> <if test="record.resourceId != null">
resource_id = #{record.resourceId,jdbcType=VARCHAR}, resource_id = #{record.resourceId,jdbcType=VARCHAR},
</if> </if>
<if test="record.valid != null">
`valid` = #{record.valid,jdbcType=BIT},
</if>
<if test="record.enablePwd != null"> <if test="record.enablePwd != null">
enable_pwd = #{record.enablePwd,jdbcType=BIT}, enable_pwd = #{record.enablePwd,jdbcType=BIT},
</if> </if>
...@@ -154,6 +164,7 @@ ...@@ -154,6 +164,7 @@
<update id="updateByExample" parameterType="map"> <update id="updateByExample" parameterType="map">
update panel_link update panel_link
set resource_id = #{record.resourceId,jdbcType=VARCHAR}, set resource_id = #{record.resourceId,jdbcType=VARCHAR},
`valid` = #{record.valid,jdbcType=BIT},
enable_pwd = #{record.enablePwd,jdbcType=BIT}, enable_pwd = #{record.enablePwd,jdbcType=BIT},
pwd = #{record.pwd,jdbcType=VARCHAR} pwd = #{record.pwd,jdbcType=VARCHAR}
<if test="_parameter != null"> <if test="_parameter != null">
...@@ -163,6 +174,9 @@ ...@@ -163,6 +174,9 @@
<update id="updateByPrimaryKeySelective" parameterType="io.dataease.base.domain.PanelLink"> <update id="updateByPrimaryKeySelective" parameterType="io.dataease.base.domain.PanelLink">
update panel_link update panel_link
<set> <set>
<if test="valid != null">
`valid` = #{valid,jdbcType=BIT},
</if>
<if test="enablePwd != null"> <if test="enablePwd != null">
enable_pwd = #{enablePwd,jdbcType=BIT}, enable_pwd = #{enablePwd,jdbcType=BIT},
</if> </if>
...@@ -174,7 +188,8 @@ ...@@ -174,7 +188,8 @@
</update> </update>
<update id="updateByPrimaryKey" parameterType="io.dataease.base.domain.PanelLink"> <update id="updateByPrimaryKey" parameterType="io.dataease.base.domain.PanelLink">
update panel_link update panel_link
set enable_pwd = #{enablePwd,jdbcType=BIT}, set `valid` = #{valid,jdbcType=BIT},
enable_pwd = #{enablePwd,jdbcType=BIT},
pwd = #{pwd,jdbcType=VARCHAR} pwd = #{pwd,jdbcType=VARCHAR}
where resource_id = #{resourceId,jdbcType=VARCHAR} where resource_id = #{resourceId,jdbcType=VARCHAR}
</update> </update>
......
package io.dataease.controller.panel.api; package io.dataease.controller.panel.api;
import io.dataease.controller.request.panel.link.EnablePwdRequest;
import io.dataease.controller.request.panel.link.LinkRequest; import io.dataease.controller.request.panel.link.LinkRequest;
import io.dataease.controller.request.panel.link.PasswordRequest; import io.dataease.controller.request.panel.link.PasswordRequest;
import io.dataease.controller.request.panel.link.ValidateRequest;
import io.dataease.dto.panel.link.GenerateDto;
import io.dataease.dto.panel.link.ValidateDto;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.Map;
@Api(tags = "仪表板:链接管理") @Api(tags = "仪表板:链接管理")
@RequestMapping("/api/link") @RequestMapping("/api/link")
public interface LinkApi { public interface LinkApi {
@ApiOperation("设置密码") @ApiOperation("重制密码")
@PostMapping("/replacePwd") @PostMapping("/resetPwd")
void replacePwd(PasswordRequest request); void replacePwd(PasswordRequest request);
@ApiOperation("生成") @ApiOperation("启用密码")
@PostMapping("/generate") @PostMapping("/enablePwd")
void generateLink(LinkRequest request); void enablePwd(EnablePwdRequest request);
@ApiOperation("切换开关")
@PostMapping("/switchLink")
void switchLink(LinkRequest request);
@ApiOperation("当前链接信息")
@PostMapping("/currentGenerate/{resourceId}")
GenerateDto currentGenerate(String resourceId);
@ApiOperation("验证访问")
@PostMapping("/validate")
ValidateDto validate(Map<String, String> param);
@ApiOperation("验证密码")
@PostMapping("/validatePwd")
boolean validatePwd(PasswordRequest request);
} }
package io.dataease.controller.panel.server; package io.dataease.controller.panel.server;
import com.google.gson.Gson;
import io.dataease.base.domain.PanelLink; import io.dataease.base.domain.PanelLink;
import io.dataease.commons.utils.ServletUtils;
import io.dataease.controller.panel.api.LinkApi; import io.dataease.controller.panel.api.LinkApi;
import io.dataease.controller.request.panel.link.EnablePwdRequest;
import io.dataease.controller.request.panel.link.LinkRequest; import io.dataease.controller.request.panel.link.LinkRequest;
import io.dataease.controller.request.panel.link.PasswordRequest; import io.dataease.controller.request.panel.link.PasswordRequest;
import io.dataease.controller.request.panel.link.ValidateRequest;
import io.dataease.dto.panel.link.GenerateDto;
import io.dataease.dto.panel.link.ValidateDto;
import io.dataease.service.panel.PanelLinkService; import io.dataease.service.panel.PanelLinkService;
import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@RestController @RestController
public class LinkServer implements LinkApi { public class LinkServer implements LinkApi {
...@@ -33,8 +34,47 @@ public class LinkServer implements LinkApi { ...@@ -33,8 +34,47 @@ public class LinkServer implements LinkApi {
} }
@Override @Override
public void generateLink(@RequestBody LinkRequest request) { public void enablePwd(@RequestBody EnablePwdRequest request) {
panelLinkService.generator(request); panelLinkService.changeEnablePwd(request);
}
@Override
public void switchLink(@RequestBody LinkRequest request) {
panelLinkService.changeValid(request);
}
@Override
public GenerateDto currentGenerate(@PathVariable("resourceId") String resourceId) {
return panelLinkService.currentGenerate(resourceId);
} }
@Override
public ValidateDto validate(@RequestBody Map<String, String> param) {
String link = param.get("link");
String json = null;
try {
json = panelLinkService.decryptParam(link);
} catch (Exception e) {
e.printStackTrace();
}
Gson gson = new Gson();
ValidateRequest request = gson.fromJson(json, ValidateRequest.class);
ValidateDto dto = new ValidateDto();
String resourceId = request.getResourceId();
PanelLink one = panelLinkService.findOne(resourceId);
if (ObjectUtils.isEmpty(one)){
dto.setValid(false);
return dto;
}
dto.setValid(one.getValid());
dto.setEnablePwd(one.getEnablePwd());
dto.setPassPwd(panelLinkService.validateHeads(one));
return dto;
}
@Override
public boolean validatePwd(@RequestBody PasswordRequest request) {
return panelLinkService.validatePwd(request);
}
} }
package io.dataease.controller.request.panel.link;
import lombok.Data;
@Data
public class EnablePwdRequest {
private String resourceId;
private boolean enablePwd;
}
...@@ -7,9 +7,5 @@ public class LinkRequest { ...@@ -7,9 +7,5 @@ public class LinkRequest {
private String resourceId; private String resourceId;
private String password; private boolean valid;
private Boolean enablePwd;
private String uri;
} }
package io.dataease.controller.request.panel.link;
import lombok.Data;
@Data
public class ValidateRequest {
private String resourceId;
private Long time;
private String salt;
}
package io.dataease.dto.panel.link;
import lombok.Data;
@Data
public class GenerateDto {
private boolean valid;
private boolean enablePwd;
private String uri;
private String pwd;
}
package io.dataease.dto.panel.link;
import lombok.Data;
@Data
public class ValidateDto {
private boolean valid;
private boolean enablePwd;
private boolean passPwd;
}
package io.dataease.service.panel; package io.dataease.service.panel;
import com.google.gson.Gson;
import io.dataease.auth.config.RsaProperties;
import io.dataease.auth.util.JWTUtils;
import io.dataease.auth.util.RsaUtil;
import io.dataease.base.domain.PanelLink; import io.dataease.base.domain.PanelLink;
import io.dataease.base.mapper.PanelLinkMapper; import io.dataease.base.mapper.PanelLinkMapper;
import io.dataease.commons.constants.AuthConstants;
import io.dataease.commons.utils.ServletUtils;
import io.dataease.controller.request.panel.link.EnablePwdRequest;
import io.dataease.controller.request.panel.link.LinkRequest; import io.dataease.controller.request.panel.link.LinkRequest;
import io.dataease.controller.request.panel.link.PasswordRequest; import io.dataease.controller.request.panel.link.PasswordRequest;
import io.dataease.dto.panel.link.GenerateDto;
import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
@Service @Service
public class PanelLinkService { public class PanelLinkService {
@Value("${public-link-url:http://localhost:8081/link?link=}")
private String baseUrl;
@Value("${public-link-salt:DataEaseLinkSalt}")
private String salt;
@Resource @Resource
private PanelLinkMapper mapper; private PanelLinkMapper mapper;
public void generator(LinkRequest request){ public void changeValid(LinkRequest request){
String resourceId = request.getResourceId();
PanelLink panelLink = mapper.selectByPrimaryKey(resourceId);
PanelLink po = new PanelLink(); PanelLink po = new PanelLink();
po.setResourceId(resourceId); po.setResourceId(request.getResourceId());
po.setEnablePwd(request.getEnablePwd()); po.setValid(request.isValid());
po.setPwd(request.getPassword()); mapper.updateByPrimaryKeySelective(po);
if (ObjectUtils.isEmpty(panelLink)){ }
mapper.insert(po);
}else{ public void changeEnablePwd(EnablePwdRequest request){
mapper.updateByPrimaryKey(po); PanelLink po = new PanelLink();
} po.setResourceId(request.getResourceId());
po.setEnablePwd(request.isEnablePwd());
mapper.updateByPrimaryKeySelective(po);
} }
public void password(PasswordRequest request){ public void password(PasswordRequest request){
...@@ -40,7 +60,80 @@ public class PanelLinkService { ...@@ -40,7 +60,80 @@ public class PanelLinkService {
return panelLink; return panelLink;
} }
public GenerateDto currentGenerate(String resourceId) {
PanelLink one = findOne(resourceId);
if (ObjectUtils.isEmpty(one)) {
one = new PanelLink();
one.setPwd(null);
one.setResourceId(resourceId);
one.setValid(false);
one.setEnablePwd(false);
mapper.insert(one);
}
return convertDto(one);
}
public String decryptParam(String text) throws Exception {
return RsaUtil.decryptByPrivateKey(RsaProperties.privateKey, text);
}
// 使用共钥加密
private String encrypt(String sourceValue) {
try {
return RsaUtil.encryptByPublicKey(RsaProperties.publicKey, sourceValue);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private String buildLinkParam(String resourceId){
Map<String,Object> map = new HashMap<>();
map.put("resourceId", resourceId);
map.put("time", System.currentTimeMillis());
map.put("salt", salt);
Gson gson = new Gson();
String encrypt = encrypt(gson.toJson(map));
String s = null;
try {
s = RsaUtil.decryptByPrivateKey(RsaProperties.privateKey, encrypt);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(s);
return encrypt;
}
private GenerateDto convertDto(PanelLink linl){
GenerateDto result = new GenerateDto();
result.setValid(linl.getValid());
result.setEnablePwd(linl.getEnablePwd());
result.setPwd(linl.getPwd());
result.setUri(baseUrl+buildLinkParam(linl.getResourceId()));
return result;
}
// 验证请求头部携带的信息 如果正确说明通过密码验证 否则没有通过
public Boolean validateHeads(PanelLink panelLink){
HttpServletRequest request = ServletUtils.request();
String token = request.getHeader("LINK-PWD-TOKEN");
if (StringUtils.isEmpty(token)) return false;
boolean verify = JWTUtils.verifyLink(token, panelLink.getResourceId(), panelLink.getPwd());
return verify;
}
public boolean validatePwd(PasswordRequest request) {
String password = request.getPassword();
String resourceId = request.getResourceId();
PanelLink one = findOne(resourceId);
String pwd = one.getPwd();
boolean pass = StringUtils.equals(pwd, password);
if (pass){
String token = JWTUtils.signLink(resourceId, password);
HttpServletResponse httpServletResponse = ServletUtils.response();
httpServletResponse.addHeader("Access-Control-Expose-Headers", "LINK-PWD-TOKEN");
httpServletResponse.setHeader("LINK-PWD-TOKEN", token);
}
return pass;
}
} }
...@@ -52,6 +52,7 @@ management.endpoints.web.exposure.include=* ...@@ -52,6 +52,7 @@ management.endpoints.web.exposure.include=*
#spring.freemarker.checkTemplateLocation=false #spring.freemarker.checkTemplateLocation=false
#RSA非对称加密参数:私钥 #RSA非对称加密参数:私钥
rsa.private_key=MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEA0vfvyTdGJkdbHkB8mp0f3FE0GYP3AYPaJF7jUd1M0XxFSE2ceK3k2kw20YvQ09NJKk+OMjWQl9WitG9pB6tSCQIDAQABAkA2SimBrWC2/wvauBuYqjCFwLvYiRYqZKThUS3MZlebXJiLB+Ue/gUifAAKIg1avttUZsHBHrop4qfJCwAI0+YRAiEA+W3NK/RaXtnRqmoUUkb59zsZUBLpvZgQPfj1MhyHDz0CIQDYhsAhPJ3mgS64NbUZmGWuuNKp5coY2GIj/zYDMJp6vQIgUueLFXv/eZ1ekgz2Oi67MNCk5jeTF2BurZqNLR3MSmUCIFT3Q6uHMtsB9Eha4u7hS31tj1UWE+D+ADzp59MGnoftAiBeHT7gDMuqeJHPL4b+kC+gzV4FGTfhR9q3tTbklZkD2A== rsa.private_key=MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEA0vfvyTdGJkdbHkB8mp0f3FE0GYP3AYPaJF7jUd1M0XxFSE2ceK3k2kw20YvQ09NJKk+OMjWQl9WitG9pB6tSCQIDAQABAkA2SimBrWC2/wvauBuYqjCFwLvYiRYqZKThUS3MZlebXJiLB+Ue/gUifAAKIg1avttUZsHBHrop4qfJCwAI0+YRAiEA+W3NK/RaXtnRqmoUUkb59zsZUBLpvZgQPfj1MhyHDz0CIQDYhsAhPJ3mgS64NbUZmGWuuNKp5coY2GIj/zYDMJp6vQIgUueLFXv/eZ1ekgz2Oi67MNCk5jeTF2BurZqNLR3MSmUCIFT3Q6uHMtsB9Eha4u7hS31tj1UWE+D+ADzp59MGnoftAiBeHT7gDMuqeJHPL4b+kC+gzV4FGTfhR9q3tTbklZkD2A==
rsa.public_key=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANL378k3RiZHWx5AfJqdH9xRNBmD9wGD2iRe41HdTNF8RUhNnHit5NpMNtGL0NPTSSpPjjI1kJfVorRvaQerUgkCAwEAAQ==
spring.cache.type=ehcache spring.cache.type=ehcache
spring.cache.ehcache.config=classpath:/ehcache/ehcache.xml spring.cache.ehcache.config=classpath:/ehcache/ehcache.xml
#打印URL路径 #打印URL路径
...@@ -60,7 +61,6 @@ logging.level.org.springframework.boot.web=trace ...@@ -60,7 +61,6 @@ logging.level.org.springframework.boot.web=trace
spring.mvc.log-request-details=true spring.mvc.log-request-details=true
pagehelper.PageRowBounds=true pagehelper.PageRowBounds=true
front-url=http://localhost:9528
......
...@@ -139,6 +139,7 @@ SET FOREIGN_KEY_CHECKS = 1; ...@@ -139,6 +139,7 @@ SET FOREIGN_KEY_CHECKS = 1;
DROP TABLE IF EXISTS `panel_link`; DROP TABLE IF EXISTS `panel_link`;
CREATE TABLE `panel_link` ( CREATE TABLE `panel_link` (
`resource_id` varchar(50) NOT NULL COMMENT '资源ID', `resource_id` varchar(50) NOT NULL COMMENT '资源ID',
`valid` tinyint(1) default 0 COMMENT '启用链接',
`enable_pwd` tinyint(1) default 0 COMMENT '启用密码', `enable_pwd` tinyint(1) default 0 COMMENT '启用密码',
`pwd` varchar(255) DEFAULT NULL COMMENT '密码', `pwd` varchar(255) DEFAULT NULL COMMENT '密码',
PRIMARY KEY (`resource_id`) PRIMARY KEY (`resource_id`)
......
import request from '@/utils/request' import request from '@/utils/request'
export function validate(param) { export function validate(data) {
return request({ return request({
url: 'api/link/validate', url: 'api/link/validate',
method: 'post', method: 'post',
loading: true, loading: true,
param data
}) })
} }
export function generate(param) { export function validatePwd(data) {
return request({ return request({
url: 'api/link/generate', url: 'api/link/validatePwd',
method: 'post', method: 'post',
param data
})
}
export function setPwd(data) {
return request({
url: 'api/link/resetPwd',
method: 'post',
data
})
}
export function switchValid(data) {
return request({
url: 'api/link/switchLink',
method: 'post',
data
})
}
export function switchEnablePwd(data) {
return request({
url: 'api/link/enablePwd',
method: 'post',
data
})
}
export function loadGenerate(resourceId) {
return request({
url: 'api/link/currentGenerate/' + resourceId,
method: 'post'
}) })
} }
import Vue from 'vue' import Vue from 'vue'
import Link from './Link.vue' import Link from './Link.vue'
import router from './link-router' import router from './link-router'
import '@/styles/index.scss' // global css
import i18n from '../lang' // internationalization
import ElementUI from 'element-ui'
Vue.config.productionTip = false Vue.config.productionTip = false
Vue.use(ElementUI, {
i18n: (key, value) => i18n.t(key, value)
})
new Vue({ new Vue({
router, router,
render: h => h(Link) render: h => h(Link)
......
module.exports = { module.exports = {
TokenKey: 'Authorization', TokenKey: 'Authorization',
RefreshTokenKey: 'refreshauthorization', RefreshTokenKey: 'refreshauthorization',
LinkTokenKey: 'LINK-PWD-TOKEN',
title: 'DATA_EASE', title: 'DATA_EASE',
/** /**
......
...@@ -17,6 +17,7 @@ const getters = { ...@@ -17,6 +17,7 @@ const getters = {
table: state => state.dataset.table, table: state => state.dataset.table,
loadingMap: state => state.request.loadingMap, loadingMap: state => state.request.loadingMap,
currentPath: state => state.permission.currentPath, currentPath: state => state.permission.currentPath,
permissions: state => state.user.permissions permissions: state => state.user.permissions,
linkToken: state => state.link.linkToken
} }
export default getters export default getters
...@@ -9,7 +9,7 @@ import dataset from './modules/dataset' ...@@ -9,7 +9,7 @@ import dataset from './modules/dataset'
import chart from './modules/chart' import chart from './modules/chart'
import request from './modules/request' import request from './modules/request'
import panel from './modules/panel' import panel from './modules/panel'
import link from './modules/link'
import animation from './animation' import animation from './animation'
import compose from './compose' import compose from './compose'
import contextmenu from './contextmenu' import contextmenu from './contextmenu'
...@@ -112,7 +112,8 @@ const data = { ...@@ -112,7 +112,8 @@ const data = {
dataset, dataset,
chart, chart,
request, request,
panel panel,
link
}, },
getters getters
} }
......
const state = {
linkToken: null
}
const mutations = {
SET_LINK_TOKEN: (state, value) => {
state.linkToken = value
}
}
const actions = {
setLinkToken({ commit }, data) {
commit('SET_LINK_TOKEN', data)
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
...@@ -122,3 +122,11 @@ div:focus { ...@@ -122,3 +122,11 @@ div:focus {
padding: 10px; padding: 10px;
} }
} }
.real-input {
.el-input__inner {
height: 25px !important;
border: none !important;
}
}
...@@ -10,6 +10,7 @@ import { tryShowLoading, tryHideLoading } from './loading' ...@@ -10,6 +10,7 @@ import { tryShowLoading, tryHideLoading } from './loading'
const TokenKey = Config.TokenKey const TokenKey = Config.TokenKey
const RefreshTokenKey = Config.RefreshTokenKey const RefreshTokenKey = Config.RefreshTokenKey
const LinkTokenKey = Config.LinkTokenKey
// create an axios instance // create an axios instance
const service = axios.create({ const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
...@@ -28,6 +29,9 @@ service.interceptors.request.use( ...@@ -28,6 +29,9 @@ service.interceptors.request.use(
// please modify it according to the actual situation // please modify it according to the actual situation
config.headers[TokenKey] = getToken() config.headers[TokenKey] = getToken()
} }
if (store.getters.linkToken) {
config.headers[LinkTokenKey] = store.getters.linkToken
}
// 增加loading // 增加loading
config.loading && tryShowLoading(store.getters.currentPath) config.loading && tryShowLoading(store.getters.currentPath)
...@@ -68,6 +72,11 @@ const checkAuth = response => { ...@@ -68,6 +72,11 @@ const checkAuth = response => {
const refreshToken = response.headers[RefreshTokenKey] const refreshToken = response.headers[RefreshTokenKey]
store.dispatch('user/refreshToken', refreshToken) store.dispatch('user/refreshToken', refreshToken)
} }
if (response.headers[LinkTokenKey]) {
const linkToken = response.headers[LinkTokenKey]
store.dispatch('link/setLinkToken', linkToken)
}
} }
const checkPermission = response => { const checkPermission = response => {
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
style="width: 370px;" style="width: 370px;"
:active-value="true" :active-value="true"
:inactive-value="false" :inactive-value="false"
@change="onChange"
/> />
</el-form-item> </el-form-item>
<el-form-item label=" "> <el-form-item label=" ">
...@@ -22,10 +23,10 @@ ...@@ -22,10 +23,10 @@
</el-form-item> </el-form-item>
<el-form-item v-if="valid" label=" "> <el-form-item v-if="valid" label=" ">
<el-checkbox v-model="form.enablePwd">密码保护</el-checkbox> <el-checkbox v-model="form.enablePwd" @change="resetEnablePwd">密码保护</el-checkbox>
<span v-if="form.enablePwd" class="de-span">{{ form.pwd }}</span> <span v-if="form.enablePwd" class="de-span">{{ form.pwd }}</span>
<span v-if="form.enablePwd" class="de-span"><el-link :underline="false" type="primary">重置</el-link></span> <span v-if="form.enablePwd" class="de-span" @click="resetPwd"><el-link :underline="false" type="primary">重置</el-link></span>
</el-form-item> </el-form-item>
<div v-if="valid" class="auth-root-class"> <div v-if="valid" class="auth-root-class">
...@@ -41,6 +42,10 @@ ...@@ -41,6 +42,10 @@
</div> </div>
</template> </template>
<script> <script>
import { loadGenerate, setPwd, switchValid, switchEnablePwd } from '@/api/link'
import { encrypt, decrypt } from '@/utils/rsaEncrypt'
export default { export default {
name: 'LinkGenerate', name: 'LinkGenerate',
...@@ -55,20 +60,75 @@ export default { ...@@ -55,20 +60,75 @@ export default {
}, },
data() { data() {
return { return {
pwdNums: 4,
valid: false, valid: false,
form: {}, form: {},
defaultForm: { enablePwd: false, pwd: '000000', uri: 'http://baidu.com' } defaultForm: { enablePwd: false, pwd: null, uri: null }
} }
}, },
created() { created() {
this.form = this.defaultForm this.form = this.defaultForm
this.currentGenerate()
}, },
methods: { methods: {
currentGenerate() {
loadGenerate(this.resourceId).then(res => {
const { valid, enablePwd, pwd, uri } = res.data
this.valid = valid
this.form.enablePwd = enablePwd
this.form.uri = uri
// 返回的密码是共钥加密后的 所以展示需要私钥解密一波
pwd && (this.form.pwd = decrypt(pwd))
})
},
createPwd() {
const randomNum = () => {
return Math.floor(Math.random() * 10) + ''
}
let result = ''
for (let index = 0; index < this.pwdNums; index++) {
result += randomNum()
}
return result
},
resetPwd() {
// 密码采用RSA共钥加密
const newPwd = this.createPwd()
const param = {
resourceId: this.resourceId,
password: encrypt(newPwd)
}
setPwd(param).then(res => {
this.form.pwd = newPwd
})
},
resetEnablePwd(value) {
const param = {
resourceId: this.resourceId,
enablePwd: value
}
switchEnablePwd(param).then(res => {
// 当切换到启用密码保护 如果没有密码 要生成密码
value && !this.form.pwd && this.resetPwd()
})
},
onCopy(e) { onCopy(e) {
// alert('You just copied: ' + e.text) // alert('You just copied: ' + e.text)
}, },
onError(e) { onError(e) {
// alert('Failed to copy texts') // alert('Failed to copy texts')
},
onChange(value) {
const param = {
resourceId: this.resourceId,
valid: value
}
switchValid(param).then(res => {
})
} }
} }
} }
......
<template> <template>
<div> <div>
<link-error v-if="showIndex===0" /> <link-error v-if="showIndex===0" />
<link-pwd v-if="showIndex===1" /> <link-pwd v-if="showIndex===1" :resource-id="resourceId" />
<link-view v-if="showIndex===2" /> <link-view v-if="showIndex===2" />
</div> </div>
</template> </template>
...@@ -29,24 +29,23 @@ export default { ...@@ -29,24 +29,23 @@ export default {
loadInit() { loadInit() {
this.link = getQueryVariable(this.PARAMKEY) this.link = getQueryVariable(this.PARAMKEY)
validate(this.buildParam()).then(res => { validate({ link: this.link }).then(res => {
const { valid, enablePwd, passPwd } = res.data const { valid, enablePwd, passPwd } = res.data
// 如果链接无效 直接显示无效页面 // 如果链接无效 直接显示无效页面
!valid && this.showError() if (!valid) {
this.showError()
return
}
// 如果未启用密码 直接显示视图页面 if (enablePwd && !passPwd) {
!enablePwd && this.showView() this.showPwd()
return
// 如果启用密码 但是未通过密码验证 显示密码框 }
!passPwd && this.showPwd()
this.showView() this.showView()
}) })
console.log(this.link)
},
buildParam() {
return {}
}, },
// 显示无效链接 // 显示无效链接
showError() { showError() {
this.showIndex = 0 this.showIndex = 0
......
<template> <template>
<div> <div class="pwd-body">
我是密码页面 <div class="pwd-wrapper">
<div class="pwd-content">
<div class="span-header">
<div class="bi-text">
请输入密码打开链接
</div>
</div>
<div class="input-layout">
<div class="input-main">
<div class="div-input">
<el-input v-model="pwd" class="real-input" />
</div>
</div>
<div class="abs-input">
<div class="input-text">{{ msg }}</div>
</div>
</div>
<div class="auth-root-class">
<span slot="footer">
<el-button size="mini" type="primary" @click="validatePwd">确定</el-button>
</span>
</div>
</div>
</div>
</div> </div>
</template> </template>
<script> <script>
import { validatePwd } from '@/api/link'
export default { export default {
name: 'LinkPwd', name: 'LinkPwd',
props: {
resourceId: {
type: String,
default: null
}
},
data() { data() {
return { return {
pwd: null,
msg: null
} }
}, },
methods: { methods: {
// 验证密码是否正确 如果正确 设置请求头部带LINK-PWD-TOKEN=entrypt(pwd)再刷新页面
refresh() {
validatePwd({ password: this.pwd, resourceId: this.resourceId }).then(res => {
if (!res.data) {
this.msg = '密码错误'
} else {
window.location.reload()
}
})
}
} }
} }
</script> </script>
<style lang="scss" scoped>
.pwd-body {
position: absolute;
width: 100%;
margin: 0;
padding: 0;
top: 0;
left: 0;
background-repeat: repeat;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-o-user-select: none;
user-select: none;
color: #3d4d66;
font: normal 12px Helvetica Neue,Arial,PingFang SC,Hiragino Sans GB,Microsoft YaHei,微软雅黑,Heiti,黑体,sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-decoration: none;
-kthml-user-focus: normal;
-moz-user-focus: normal;
-moz-outline: 0 none;
outline: 0 none;
height: 100%;
display: block;
}
.pwd-wrapper {
background-color: #F7F8FA;
height: 100%;
justify-content: center !important;
align-items: center !important;
min-height: 25px;
display: flex;
-moz-flex-direction: row;
-o-flex-direction: row;
flex-direction: row;
-moz-justify-content: flex-start;
-ms-justify-content: flex-start;
-o-justify-content: flex-start;
justify-content: flex-start;
-moz-align-items: flex-start;
-ms-align-items: flex-start;
-o-align-items: flex-start;
align-items: flex-start;
-o-flex-wrap: nowrap;
flex-wrap: nowrap;
}
.pwd-content {
width: 450px;
height: 250px;
position: relative;
flex-shrink: 0;
background-color: #FFFFFF;
display: block;
}
.span-header {
position: relative;
margin: 57px auto 0px;
justify-content: center !important;
align-items: center !important;
}
.bi-text {
max-width: 100%;
text-align: center;
white-space: pre;
text-overflow: ellipsis;
position: relative;
flex-shrink: 0;
box-sizing: border-box;
overflow: hidden;
overflow-x: hidden;
overflow-y: hidden;
word-break: break-all;
display: block;
}
.input-layout{
width: 152px;
position: relative;
margin: 0px auto;
padding: 0;
display: block;
}
.input-main {
width: 150px;
height: 30px;
position: relative;
margin-top: 30px;
border: 1px solid #e8eaed;
display: block;
}
.div-input {
inset: 2px 4px;
position: absolute;
display: block;
}
.abs-input {
height: 20px;
position: relative;
margin-top: 5px;
display: block;
}
.input-text {
height: 20px;
line-height: 20px;
text-align: center;
white-space: pre;
text-overflow: ellipsis;
left: 0px;
top: 0px;
bottom: 0px;
position: absolute;
color: #E65251;
box-sizing: border-box;
}
.real-input {
width: 100%;
height: 100%;
border: none;
outline: none;
padding: 0px;
margin: 0px;
inset: 0px;
position: absolute;
display: block;
}
.auth-root-class {
margin: 15px 0px 5px;
text-align: center;
}
</style>
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论