From f77c3f7ebf83e6b2d75599adf8d0454572a46aa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8A=9B?= Date: Fri, 11 Feb 2022 17:35:07 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../annotation/rest/AnonymousPostMapping.java | 59 ++++++ .../com/canvas/web/config/RsaProperties.java | 17 ++ .../java/com/canvas/web/utils/RsaUtils.java | 177 ++++++++++++++++++ .../controller/AuthorizationController.java | 86 +++++++++ .../security/service/OnlineUserService.java | 25 +++ .../security/service/dto/OnlineUserDto.java | 54 ++++++ 6 files changed, 418 insertions(+) create mode 100644 common/src/main/java/com/canvas/web/annotation/rest/AnonymousPostMapping.java create mode 100644 common/src/main/java/com/canvas/web/config/RsaProperties.java create mode 100644 common/src/main/java/com/canvas/web/utils/RsaUtils.java create mode 100644 system/src/main/java/modules/security/controller/AuthorizationController.java create mode 100644 system/src/main/java/modules/security/service/dto/OnlineUserDto.java diff --git a/common/src/main/java/com/canvas/web/annotation/rest/AnonymousPostMapping.java b/common/src/main/java/com/canvas/web/annotation/rest/AnonymousPostMapping.java new file mode 100644 index 0000000..6584ff4 --- /dev/null +++ b/common/src/main/java/com/canvas/web/annotation/rest/AnonymousPostMapping.java @@ -0,0 +1,59 @@ +package com.canvas.web.annotation.rest; + + +import com.canvas.web.annotation.AnonymousAccess; +import org.springframework.core.annotation.AliasFor; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +import java.lang.annotation.*; + +@AnonymousAccess +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@RequestMapping(method = RequestMethod.POST) +public @interface AnonymousPostMapping { + + /** + * Alias for {@link RequestMapping#name}. + */ + @AliasFor(annotation = RequestMapping.class) + String name() default ""; + + /** + * Alias for {@link RequestMapping#value}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] value() default {}; + + /** + * Alias for {@link RequestMapping#path}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] path() default {}; + + /** + * Alias for {@link RequestMapping#params}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] params() default {}; + + /** + * Alias for {@link RequestMapping#headers}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] headers() default {}; + + /** + * Alias for {@link RequestMapping#consumes}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] consumes() default {}; + + /** + * Alias for {@link RequestMapping#produces}. + */ + @AliasFor(annotation = RequestMapping.class) + String[] produces() default {}; +} diff --git a/common/src/main/java/com/canvas/web/config/RsaProperties.java b/common/src/main/java/com/canvas/web/config/RsaProperties.java new file mode 100644 index 0000000..07d11c9 --- /dev/null +++ b/common/src/main/java/com/canvas/web/config/RsaProperties.java @@ -0,0 +1,17 @@ +package com.canvas.web.config; + +import lombok.Data; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Data +@Component +public class RsaProperties { + + public static String privateKey; + + @Value("${rsa.private_key}") + public void setPrivateKey(String privateKey) { + RsaProperties.privateKey = privateKey; + } +} diff --git a/common/src/main/java/com/canvas/web/utils/RsaUtils.java b/common/src/main/java/com/canvas/web/utils/RsaUtils.java new file mode 100644 index 0000000..d14ded7 --- /dev/null +++ b/common/src/main/java/com/canvas/web/utils/RsaUtils.java @@ -0,0 +1,177 @@ +package com.canvas.web.utils; + +import org.apache.commons.codec.binary.Base64; + +import javax.crypto.Cipher; +import java.security.*; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +public class RsaUtils { + + private static final String SRC = "123456"; + + public static void main(String[] args) throws Exception { + System.out.println("\n"); + RsaKeyPair keyPair = generateKeyPair(); + System.out.println("公钥:" + keyPair.getPublicKey()); + System.out.println("私钥:" + keyPair.getPrivateKey()); + System.out.println("\n"); + test1(keyPair); + System.out.println("\n"); + test2(keyPair); + System.out.println("\n"); + } + + /** + * 公钥加密私钥解密 + */ + private static void test1(RsaKeyPair keyPair) throws Exception { + System.out.println("***************** 公钥加密私钥解密开始 *****************"); + String text1 = encryptByPublicKey(keyPair.getPublicKey(), RsaUtils.SRC); + String text2 = decryptByPrivateKey(keyPair.getPrivateKey(), text1); + System.out.println("加密前:" + RsaUtils.SRC); + System.out.println("加密后:" + text1); + System.out.println("解密后:" + text2); + if (RsaUtils.SRC.equals(text2)) { + System.out.println("解密字符串和原始字符串一致,解密成功"); + } else { + System.out.println("解密字符串和原始字符串不一致,解密失败"); + } + System.out.println("***************** 公钥加密私钥解密结束 *****************"); + } + + /** + * 私钥加密公钥解密 + * @throws Exception / + */ + private static void test2(RsaKeyPair keyPair) throws Exception { + System.out.println("***************** 私钥加密公钥解密开始 *****************"); + String text1 = encryptByPrivateKey(keyPair.getPrivateKey(), RsaUtils.SRC); + String text2 = decryptByPublicKey(keyPair.getPublicKey(), text1); + System.out.println("加密前:" + RsaUtils.SRC); + System.out.println("加密后:" + text1); + System.out.println("解密后:" + text2); + if (RsaUtils.SRC.equals(text2)) { + System.out.println("解密字符串和原始字符串一致,解密成功"); + } else { + System.out.println("解密字符串和原始字符串不一致,解密失败"); + } + System.out.println("***************** 私钥加密公钥解密结束 *****************"); + } + + /** + * 公钥解密 + * + * @param publicKeyText 公钥 + * @param text 待解密的信息 + * @return / + * @throws Exception / + */ + public static String decryptByPublicKey(String publicKeyText, String text) throws Exception { + X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKeyText)); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec); + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.DECRYPT_MODE, publicKey); + byte[] result = cipher.doFinal(Base64.decodeBase64(text)); + return new String(result); + } + + /** + * 私钥加密 + * + * @param privateKeyText 私钥 + * @param text 待加密的信息 + * @return / + * @throws Exception / + */ + public static String encryptByPrivateKey(String privateKeyText, String text) throws Exception { + PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyText)); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec); + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.ENCRYPT_MODE, privateKey); + byte[] result = cipher.doFinal(text.getBytes()); + return Base64.encodeBase64String(result); + } + + /** + * 私钥解密 + * + * @param privateKeyText 私钥 + * @param text 待解密的文本 + * @return / + * @throws Exception / + */ + public static String decryptByPrivateKey(String privateKeyText, String text) throws Exception { + PKCS8EncodedKeySpec pkcs8EncodedKeySpec5 = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKeyText)); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec5); + Cipher cipher = Cipher.getInstance("RSA"); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + byte[] result = cipher.doFinal(Base64.decodeBase64(text)); + 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()); + return Base64.encodeBase64String(result); + } + + /** + * 构建RSA密钥对 + * + * @return / + * @throws NoSuchAlgorithmException / + */ + public static RsaKeyPair generateKeyPair() throws NoSuchAlgorithmException { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(1024); + KeyPair keyPair = keyPairGenerator.generateKeyPair(); + RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic(); + RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate(); + String publicKeyString = Base64.encodeBase64String(rsaPublicKey.getEncoded()); + String privateKeyString = Base64.encodeBase64String(rsaPrivateKey.getEncoded()); + return new RsaKeyPair(publicKeyString, privateKeyString); + } + + + /** + * RSA密钥对对象 + */ + public static class RsaKeyPair { + + private final String publicKey; + private final String privateKey; + + public RsaKeyPair(String publicKey, String privateKey) { + this.publicKey = publicKey; + this.privateKey = privateKey; + } + + public String getPublicKey() { + return publicKey; + } + + public String getPrivateKey() { + return privateKey; + } + + } + +} diff --git a/system/src/main/java/modules/security/controller/AuthorizationController.java b/system/src/main/java/modules/security/controller/AuthorizationController.java new file mode 100644 index 0000000..46863be --- /dev/null +++ b/system/src/main/java/modules/security/controller/AuthorizationController.java @@ -0,0 +1,86 @@ +package modules.security.controller; + + +import com.canvas.web.annotation.rest.AnonymousPostMapping; +import com.canvas.web.config.RsaProperties; +import com.canvas.web.exception.BaseException; +import com.canvas.web.utils.RedisUtils; +import com.canvas.web.utils.RsaUtils; +import com.canvas.web.utils.StringUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import modules.security.config.bean.LoginProperties; +import modules.security.config.bean.SecurityProperties; +import modules.security.security.TokenProvider; +import modules.security.service.OnlineUserService; +import modules.security.service.dto.AuthUserDto; +import modules.security.service.dto.JwtUserDto; +import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import java.util.HashMap; +import java.util.Map; + +@Slf4j +@RestController +@RequestMapping("/auth") +@RequiredArgsConstructor +@Api(tags = "系统:系统授权接口") +public class AuthorizationController { + + private final SecurityProperties properties; + private final RedisUtils redisUtils; + private final OnlineUserService onlineUserService; + private final TokenProvider tokenProvider; + private final AuthenticationManagerBuilder authenticationManagerBuilder; + + @Resource + private LoginProperties loginProperties; + + + @ApiOperation("登录授权") + @AnonymousPostMapping(value = "/login") + public ResponseEntity login(@Validated @RequestBody AuthUserDto authUser, HttpServletRequest request) throws Exception { + // 密码解密 + String password = RsaUtils.decryptByPrivateKey(RsaProperties.privateKey, authUser.getPassword()); + // 查询验证码 + String code = (String) redisUtils.get(authUser.getUuid()); + // 清除验证码 + redisUtils.del(authUser.getUuid()); + if (StringUtils.isBlank(code)) { + throw new BaseException("验证码不存在或已过期"); + } + if (StringUtils.isBlank(authUser.getCode()) || !authUser.getCode().equalsIgnoreCase(code)) { + throw new BaseException("验证码错误"); + } + UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(authUser.getUsername(), password); + Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken); + SecurityContextHolder.getContext().setAuthentication(authentication); + // 生成令牌 + String token = tokenProvider.createToken(authentication); + final JwtUserDto jwtUserDto = (JwtUserDto) authentication.getPrincipal(); + // 保存在线信息 + onlineUserService.save(jwtUserDto, token, request); + // 返回 token 与 用户信息 + Map authInfo = new HashMap(2) {{ + put("token", properties.getTokenStartWith() + token); + put("user", jwtUserDto); + }}; + if (loginProperties.isSingleLogin()) { + //踢掉之前已经登录的token + onlineUserService.checkLoginOnUser(authUser.getUsername(), token); + } + return ResponseEntity.ok(authInfo); + } +} diff --git a/system/src/main/java/modules/security/service/OnlineUserService.java b/system/src/main/java/modules/security/service/OnlineUserService.java index 5a97116..a213cc7 100644 --- a/system/src/main/java/modules/security/service/OnlineUserService.java +++ b/system/src/main/java/modules/security/service/OnlineUserService.java @@ -1,10 +1,16 @@ package modules.security.service; import com.canvas.web.utils.RedisUtils; +import com.canvas.web.utils.StringUtils; import lombok.extern.slf4j.Slf4j; import modules.security.config.bean.SecurityProperties; +import modules.security.service.dto.JwtUserDto; +import modules.security.service.dto.OnlineUserDto; import org.springframework.stereotype.Service; +import javax.servlet.http.HttpServletRequest; +import java.util.Date; + @Service @Slf4j @@ -16,4 +22,23 @@ public class OnlineUserService { this.properties = properties; this.redisUtils = redisUtils; } + + public void save(JwtUserDto jwtUserDto, String token, HttpServletRequest request) { + + //String dept = jwtUserDto.getUser().getName().getDept(); + String ip = StringUtils.getIp(request); + String browser = StringUtils.getBrowser(request); + String address = StringUtils.getCityInfo(ip); + OnlineUserDto onlineUserDto = null; + try { + //onlineUserDto = new OnlineUserDto(jwtUserDto.getUsername(), jwtUserDto.getUser().getNickName(), dept, browser , ip, address, EncryptUtils.desEncrypt(token), new Date()); + } catch (Exception e) { + log.error(e.getMessage(),e); + } + redisUtils.set(properties.getOnlineKey() + token, onlineUserDto, properties.getTokenValidityInSeconds()/1000); + } + + public void checkLoginOnUser(String username, String token) { + + } } diff --git a/system/src/main/java/modules/security/service/dto/OnlineUserDto.java b/system/src/main/java/modules/security/service/dto/OnlineUserDto.java new file mode 100644 index 0000000..8dca55e --- /dev/null +++ b/system/src/main/java/modules/security/service/dto/OnlineUserDto.java @@ -0,0 +1,54 @@ +package modules.security.service.dto; + + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Date; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class OnlineUserDto { + + /** + * 用户名 + */ + private String userName; + + /** + * 昵称 + */ + private String nickName; + + /** + * 岗位 + */ + private String dept; + + /** + * 浏览器 + */ + private String browser; + + /** + * IP + */ + private String ip; + + /** + * 地址 + */ + private String address; + + /** + * token + */ + private String key; + + /** + * 登录时间 + */ + private Date loginTime; +}