|
@@ -18,29 +18,29 @@
|
|
|
package org.dromara.maxkey.web.contorller;
|
|
|
|
|
|
import java.awt.image.BufferedImage;
|
|
|
-import java.util.HashMap;
|
|
|
|
|
|
-import org.apache.commons.codec.binary.Hex;
|
|
|
import org.apache.commons.lang3.StringUtils;
|
|
|
import org.dromara.maxkey.authn.annotation.CurrentUser;
|
|
|
import org.dromara.maxkey.crypto.Base32Utils;
|
|
|
import org.dromara.maxkey.crypto.Base64Utils;
|
|
|
import org.dromara.maxkey.crypto.password.PasswordReciprocal;
|
|
|
import org.dromara.maxkey.entity.Message;
|
|
|
+import org.dromara.maxkey.entity.dto.TimeBasedDto;
|
|
|
import org.dromara.maxkey.entity.idm.UserInfo;
|
|
|
import org.dromara.maxkey.password.onetimepwd.algorithm.OtpKeyUriFormat;
|
|
|
import org.dromara.maxkey.password.onetimepwd.algorithm.OtpSecret;
|
|
|
import org.dromara.maxkey.password.onetimepwd.impl.TimeBasedOtpAuthn;
|
|
|
import org.dromara.maxkey.persistence.service.UserInfoService;
|
|
|
-import org.dromara.maxkey.util.RQCodeUtils;
|
|
|
+import org.dromara.maxkey.util.QRCodeUtils;
|
|
|
import org.slf4j.Logger;
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
-import org.springframework.http.ResponseEntity;
|
|
|
-import org.springframework.stereotype.Controller;
|
|
|
+import org.springframework.web.bind.annotation.GetMapping;
|
|
|
+import org.springframework.web.bind.annotation.PutMapping;
|
|
|
+import org.springframework.web.bind.annotation.RequestBody;
|
|
|
import org.springframework.web.bind.annotation.RequestMapping;
|
|
|
import org.springframework.web.bind.annotation.RequestParam;
|
|
|
-import org.springframework.web.bind.annotation.ResponseBody;
|
|
|
+import org.springframework.web.bind.annotation.RestController;
|
|
|
|
|
|
|
|
|
/**
|
|
@@ -48,8 +48,8 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
|
|
* @author Crystal.Sea
|
|
|
*
|
|
|
*/
|
|
|
-@Controller
|
|
|
-@RequestMapping(value = { "/config" })
|
|
|
+@RestController
|
|
|
+@RequestMapping(value = { "/config/timebased" })
|
|
|
public class OneTimePasswordController {
|
|
|
static final Logger logger = LoggerFactory.getLogger(OneTimePasswordController.class);
|
|
|
|
|
@@ -62,55 +62,73 @@ public class OneTimePasswordController {
|
|
|
@Autowired
|
|
|
TimeBasedOtpAuthn timeBasedOtpAuthn;
|
|
|
|
|
|
- @RequestMapping(value = {"/timebased"})
|
|
|
- @ResponseBody
|
|
|
- public Message<?> timebased(
|
|
|
- @RequestParam(name="generate") String generate,
|
|
|
- @CurrentUser UserInfo currentUser) {
|
|
|
- HashMap<String,Object >timebased =new HashMap<>();
|
|
|
-
|
|
|
- generate(generate,currentUser);
|
|
|
-
|
|
|
- String sharedSecret =
|
|
|
- PasswordReciprocal.getInstance().decoder(currentUser.getSharedSecret());
|
|
|
-
|
|
|
- otpKeyUriFormat.setSecret(sharedSecret);
|
|
|
- String otpauth = otpKeyUriFormat.format(currentUser.getUsername());
|
|
|
- byte[] byteSharedSecret = Base32Utils.decode(sharedSecret);
|
|
|
- String hexSharedSecret = Hex.encodeHexString(byteSharedSecret);
|
|
|
- BufferedImage bufferedImage = RQCodeUtils.write2BufferedImage(otpauth, "gif", 300, 300);
|
|
|
- String rqCode = Base64Utils.encodeImage(bufferedImage);
|
|
|
-
|
|
|
- timebased.put("displayName", currentUser.getDisplayName());
|
|
|
- timebased.put("username", currentUser.getUsername());
|
|
|
- timebased.put("digits", otpKeyUriFormat.getDigits());
|
|
|
- timebased.put("period", otpKeyUriFormat.getPeriod());
|
|
|
- timebased.put("sharedSecret", sharedSecret);
|
|
|
- timebased.put("hexSharedSecret", hexSharedSecret);
|
|
|
- timebased.put("rqCode", rqCode);
|
|
|
- return new Message<HashMap<String,Object >>(timebased);
|
|
|
+ @GetMapping(value = {"/view"})
|
|
|
+ public Message<TimeBasedDto> view(@CurrentUser UserInfo currentUser) {
|
|
|
+ UserInfo user = userInfoService.get(currentUser.getId());
|
|
|
+ String sharedSecret = "";
|
|
|
+ String qrCode = "";
|
|
|
+ if(StringUtils.isNotBlank(user.getSharedSecret())) {
|
|
|
+ sharedSecret = PasswordReciprocal.getInstance().decoder(user.getSharedSecret());
|
|
|
+ qrCode = genQRCode(sharedSecret,currentUser.getUsername());
|
|
|
+ }
|
|
|
+ return new Message<>(
|
|
|
+ new TimeBasedDto(
|
|
|
+ user.getDisplayName(),
|
|
|
+ user.getUsername(),
|
|
|
+ otpKeyUriFormat.getDigits(),
|
|
|
+ otpKeyUriFormat.getPeriod(),
|
|
|
+ sharedSecret,
|
|
|
+ qrCode,
|
|
|
+ ""
|
|
|
+ ));
|
|
|
}
|
|
|
-
|
|
|
- public void generate(String generate,@CurrentUser UserInfo currentUser) {
|
|
|
- if((StringUtils.isNotBlank(generate)
|
|
|
- && generate.equalsIgnoreCase("YES"))
|
|
|
- ||StringUtils.isBlank(currentUser.getSharedSecret())) {
|
|
|
-
|
|
|
- byte[] byteSharedSecret = OtpSecret.generate(otpKeyUriFormat.getCrypto());
|
|
|
- String sharedSecret = Base32Utils.encode(byteSharedSecret);
|
|
|
- sharedSecret = PasswordReciprocal.getInstance().encode(sharedSecret);
|
|
|
- currentUser.setSharedSecret(sharedSecret);
|
|
|
- userInfoService.updateSharedSecret(currentUser);
|
|
|
-
|
|
|
+
|
|
|
+ @GetMapping(value = {"/generate"})
|
|
|
+ public Message<TimeBasedDto> generate(@CurrentUser UserInfo currentUser) {
|
|
|
+ //generate
|
|
|
+ byte[] byteSharedSecret = OtpSecret.generate(otpKeyUriFormat.getCrypto());
|
|
|
+ String sharedSecret = Base32Utils.encode(byteSharedSecret);
|
|
|
+ String qrCode = genQRCode(sharedSecret,currentUser.getUsername());
|
|
|
+ return new Message<>(
|
|
|
+ new TimeBasedDto(
|
|
|
+ currentUser.getDisplayName(),
|
|
|
+ currentUser.getUsername(),
|
|
|
+ otpKeyUriFormat.getDigits(),
|
|
|
+ otpKeyUriFormat.getPeriod(),
|
|
|
+ sharedSecret,
|
|
|
+ qrCode,
|
|
|
+ ""
|
|
|
+ ));
|
|
|
+ }
|
|
|
+
|
|
|
+ @PutMapping(value = {"/update"})
|
|
|
+ public Message<String> update(@RequestBody TimeBasedDto timeBasedDto , @CurrentUser UserInfo currentUser) {
|
|
|
+ // 从当前用户信息中获取共享密钥
|
|
|
+ UserInfo user = new UserInfo();
|
|
|
+ user.setId(currentUser.getId());
|
|
|
+ user.setSharedSecret(PasswordReciprocal.getInstance().encode(timeBasedDto.sharedSecret()));
|
|
|
+ // 计算当前时间对应的动态密码
|
|
|
+ if (StringUtils.isNotBlank(timeBasedDto.otpCode()) && timeBasedOtpAuthn.validate(user, timeBasedDto.otpCode())) {
|
|
|
+ userInfoService.updateSharedSecret(user);
|
|
|
+ return new Message<>(Message.SUCCESS);
|
|
|
+ } else {
|
|
|
+ return new Message<>(Message.FAIL);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- @RequestMapping("/verify")
|
|
|
- public Message<?> verify(@RequestParam("otp") String otp, @CurrentUser UserInfo currentUser) {
|
|
|
+ public String genQRCode(String sharedSecret,String username) {
|
|
|
+ otpKeyUriFormat.setSecret(sharedSecret);
|
|
|
+ String otpauth = otpKeyUriFormat.format(username);
|
|
|
+ BufferedImage bufferedImage = QRCodeUtils.write2BufferedImage(otpauth, "gif", 300, 300);
|
|
|
+ return Base64Utils.encodeImage(bufferedImage);
|
|
|
+ }
|
|
|
+
|
|
|
+ @GetMapping("/verify")
|
|
|
+ public Message<String> verify(@RequestParam("otpCode") String otpCode, @CurrentUser UserInfo currentUser) {
|
|
|
// 从当前用户信息中获取共享密钥
|
|
|
- String sharedSecret = PasswordReciprocal.getInstance().decoder(currentUser.getSharedSecret());
|
|
|
+ UserInfo user = userInfoService.get(currentUser.getId());
|
|
|
// 计算当前时间对应的动态密码
|
|
|
- boolean validate = timeBasedOtpAuthn.validate(currentUser, otp);
|
|
|
+ boolean validate = timeBasedOtpAuthn.validate(user, otpCode);
|
|
|
if (validate) {
|
|
|
return new Message<>(0,"One-Time Password verification succeeded");
|
|
|
} else {
|