fix: 日志入库

This commit is contained in:
wangyu 2021-01-16 00:33:34 +08:00
parent 39f57e2529
commit 37596bb702
8 changed files with 203 additions and 10 deletions

View File

@ -0,0 +1,68 @@
package com.flyfish.framework.config;
import com.flyfish.framework.config.properties.SecurityProperties;
import com.flyfish.framework.utils.RSAUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AbstractUserDetailsReactiveAuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.Assert;
import reactor.core.publisher.Mono;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
/**
* 支持rsa的认证管理器
*
* @author wangyu
*/
@Slf4j
public class RSAAuthenticationManager extends AbstractUserDetailsReactiveAuthenticationManager {
private final ReactiveUserDetailsService userDetailsService;
private final Boolean rsa;
public RSAAuthenticationManager(SecurityProperties securityProperties, ReactiveUserDetailsService userDetailsService) {
Assert.notNull(userDetailsService, "userDetailsService cannot be null");
this.rsa = securityProperties.isRsa();
this.userDetailsService = userDetailsService;
}
@Override
public Mono<Authentication> authenticate(Authentication authentication) {
if (rsa && !authentication.isAuthenticated()) {
Object credentials = authentication.getCredentials();
if (credentials instanceof String) {
Authentication mapped = createAuthentication(authentication);
return super.authenticate(mapped);
}
}
return super.authenticate(authentication);
}
private UsernamePasswordAuthenticationToken createAuthentication(Authentication authentication) throws IllegalArgumentException {
String password = (String) authentication.getCredentials();
try {
password = RSAUtils.decrypt(password, RSAKeys.PRIVATE_KEY);
} catch (IllegalBlockSizeException | InvalidKeyException | BadPaddingException | NoSuchAlgorithmException | NoSuchPaddingException e) {
log.error("尝试解密密码出错", e);
throw new IllegalArgumentException("非法请求!密码格式校验失败!");
} catch (IllegalArgumentException e) {
log.error("抛出参数异常", e);
throw new IllegalArgumentException("密码未加密,请求无效!" + e.getMessage());
}
return new UsernamePasswordAuthenticationToken(authentication.getPrincipal(), password, authentication.getAuthorities());
}
@Override
protected Mono<UserDetails> retrieveUser(String username) {
return this.userDetailsService.findByUsername(username);
}
}

View File

@ -0,0 +1,13 @@
package com.flyfish.framework.config;
/**
* rsa密钥使用PKCS8的私钥格式
* @author wangyu
*/
public interface RSAKeys {
String PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDp4mkmGRzPqM/ilnmNiVzZVVnWvRQRZ/u45xeCwPowYBa+dJWby26CtApy9bz0szCPWZkgj3AGGaepdlwRyl8c1cVXGBsewf7rRZyMTWasW7YxNCDjAnlVo+NsZUqumK1UcK4EkEtsVI32EQMe+4BrkNEFJeEbeZWfRfEU20utBQIDAQAB";
String PRIVATE_KEY = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAOniaSYZHM+oz+KWeY2JXNlVWda9FBFn+7jnF4LA+jBgFr50lZvLboK0CnL1vPSzMI9ZmSCPcAYZp6l2XBHKXxzVxVcYGx7B/utFnIxNZqxbtjE0IOMCeVWj42xlSq6YrVRwrgSQS2xUjfYRAx77gGuQ0QUl4Rt5lZ9F8RTbS60FAgMBAAECgYA9aS2iy0U9YV30aP5Ro5e1mSuVrt/uEheOXV9W/cSznwdZytLdrXS+7PcxaETNG5/MhAIpAoCvmVzkqidSpIiEUSRjtQVehOmXBL7SsiVrsLO7FtCwWhgPY8xAOrorVgM1awNiDTz1GI7yVEx8ahL1i2jky6AcFXM9VmuC6GaIoQJBAP5kSqH/TkiuSLk2Pq4dczWldpgndxYykgl4XFbIzxDg+M8a7nDtFAf74QYMiJYepdoL6fnoidk2Waw3Pt3tFK8CQQDrXO4XeebvBMOowdVnOn9lJs8pMC47ajVS1h2jtzhueEhglK1hb6+xUNHAW9uvD6h17im+SSVcEWUdV94GRi6LAkBOIzwRWjwPcwj/arwB+yXCGpq8zZJ0jP/yTLgAKBoCEzgZVSpcIVX/xcwucXcatTs4KGSr0FNXFqygiy+SNxodAkAE1mUiYGQJt5xZ5JoBRCeIKbMKUq4wlG9CI5p6WfKz+o7qRUQHA6elylZ1UK4EPBsnSVzcR16YNNdSMhMjJ+AfAkBnQQQGj+Cg5ZcU/EM3/Gh5/DQ4D6OnyLR/hekQhoKmj4B18jMtuB0IMe4lHhY1raOPsxm/6y758ULv9yHj1yaP";
}

View File

@ -21,8 +21,10 @@ import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
@ -31,7 +33,8 @@ import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.authentication.HttpStatusServerEntryPoint;
import org.springframework.security.web.server.context.ServerSecurityContextRepository;
import org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository;
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;
import static org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers.pathMatchers;
/**
* @author wangyu
@ -85,21 +88,26 @@ public class WebSecurityConfig {
* @return 结果
*/
@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http, ResultDataTransformer dataTransformer,
TokenProvider tokenProvider, SecurityProperties properties) {
@SuppressWarnings("all")
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http,
ResultDataTransformer dataTransformer,
TokenProvider tokenProvider,
SecurityProperties properties,
ReactiveUserDetailsService userDetailsService) {
http
.securityContextRepository(contextRepository())
.authorizeExchange()
.pathMatchers(properties.getAllowUris()).permitAll()
.pathMatchers("/api/logout").permitAll()
.pathMatchers("/api/logout", "/api/login").permitAll()
.pathMatchers("/api/users/**").authenticated()
.anyExchange().authenticated()
.and()
.formLogin() // 配置登录节点
.authenticationManager(authenticationManager(properties, userDetailsService))
.authenticationEntryPoint(new HttpStatusServerEntryPoint(HttpStatus.UNAUTHORIZED))
.authenticationFailureHandler(new JsonAuthenticationFailureHandler(dataTransformer))
.authenticationSuccessHandler(new JsonAuthenticationSuccessHandler(dataTransformer))
.requiresAuthenticationMatcher(ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST, "/login", "/api/login"))
.requiresAuthenticationMatcher(pathMatchers(HttpMethod.POST, "/login", "/api/login"))
.and()
.logout()
.logoutUrl("/api/logout")
@ -109,10 +117,23 @@ public class WebSecurityConfig {
return http.build();
}
/**
* 构建鉴权管理器
*
* @param userDetailsService 用户详情服务
* @return 结果
*/
public ReactiveAuthenticationManager authenticationManager(SecurityProperties securityProperties,
ReactiveUserDetailsService userDetailsService) {
RSAAuthenticationManager authenticationManager = new RSAAuthenticationManager(securityProperties, userDetailsService);
authenticationManager.setPasswordEncoder(passwordEncoder());
return authenticationManager;
}
/**
* 默认的初始化
*
* @param userService 用户服务
* @param userService 用户服务
* @return 结果
*/
@Bean

View File

@ -3,8 +3,6 @@ package com.flyfish.framework.config.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.List;
/**
* 安全配置类
*
@ -16,4 +14,7 @@ public class SecurityProperties {
// 允许的uris
private String[] allowUris = new String[0];
// 启用rsa
private boolean rsa;
}

View File

@ -1,15 +1,36 @@
package com.flyfish.framework.controller;
import com.flyfish.framework.bean.Result;
import com.flyfish.framework.config.RSAKeys;
import com.flyfish.framework.config.properties.SecurityProperties;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* 登录controller
*
* @author wangyu
*/
@RestController
@RequestMapping("/login")
public class LoginController {
@Resource
private SecurityProperties securityProperties;
/**
* 获取公钥如果有的话
*
* @return 结果
*/
@GetMapping("")
public Result<String> getPublicKey() {
if (securityProperties.isRsa()) {
return Result.ok(RSAKeys.PUBLIC_KEY);
}
return Result.error("尚未配置加密密钥!");
}
}

View File

@ -0,0 +1,67 @@
package com.flyfish.framework.utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.lang.Nullable;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
/**
* 缺省的RSA提供固定的公钥和私钥
*
* @author wangyu
*/
@Slf4j
public class RSAUtils {
@Nullable
private static PublicKey getPublicKey(String base64PublicKey) {
try {
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(base64PublicKey.getBytes()));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(keySpec);
} catch (NoSuchAlgorithmException e) {
log.error("获取公钥时出错,未找到加密算法:", e);
} catch (InvalidKeySpecException e) {
log.error("获取公钥时出错,不可用的密钥特征:", e);
}
return null;
}
@Nullable
private static PrivateKey getPrivateKey(String base64PrivateKey) {
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(base64PrivateKey.getBytes()));
try {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePrivate(keySpec);
} catch (NoSuchAlgorithmException e) {
log.error("获取私钥时出错,未找到加密算法:", e);
} catch (InvalidKeySpecException e) {
log.error("获取私钥时出错,不可用的密钥特征:", e);
}
return null;
}
public static byte[] encrypt(String data, String publicKey) throws BadPaddingException, IllegalBlockSizeException, InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, getPublicKey(publicKey));
return cipher.doFinal(data.getBytes());
}
private static String decrypt(byte[] data, PrivateKey privateKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return new String(cipher.doFinal(data));
}
public static String decrypt(String data, String base64PrivateKey) throws IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException {
return decrypt(Base64.getDecoder().decode(data.getBytes()), getPrivateKey(base64PrivateKey));
}
}

View File

@ -31,6 +31,8 @@ public class JwtSecurityContextRepository implements ServerSecurityContextReposi
URI requestURI = request.getURI();
// 存在jwt时校验jwtredis也需要存在
if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt) && redisOperations.hasKey(jwt)) {
// token即将过期续租
Authentication authentication = tokenProvider.getAuthentication(jwt);
log.debug("set Authentication to security context for '{}', uri: {}", authentication.getName(), requestURI);
return userDetailsService.findByUsername(authentication.getName())

View File

@ -2,7 +2,6 @@ package com.flyfish.framework.configuration.jwt;
import com.flyfish.framework.domain.base.IUser;
import com.flyfish.framework.utils.RedisOperations;
import com.sun.org.apache.xml.internal.security.algorithms.SignatureAlgorithm;
import io.jsonwebtoken.*;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.io.DecodingException;
@ -89,7 +88,8 @@ public class TokenProvider implements InitializingBean {
HttpHeaders headers = exchange.getResponse().getHeaders();
// app用户从头部返回方便获取
headers.add("Token", token);
headers.add("Token-Valid-Time", String.valueOf(tokenValidityInMilliseconds));
headers.add("Token-Valid-Time", String.valueOf(remember ? tokenValidityInMillisecondsForRememberMe :
tokenValidityInMilliseconds));
// token在web端的时间较短不允许记住所以使用短期
// exchange.getResponse().addCookie(ResponseCookie.from(AUTHORIZATION_HEADER, "Bearer-" + token).
// httpOnly(true).maxAge(tokenValidityInMilliseconds).build());