fix: 日志入库
This commit is contained in:
parent
39f57e2529
commit
37596bb702
@ -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);
|
||||
}
|
||||
}
|
@ -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";
|
||||
|
||||
}
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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("尚未配置加密密钥!");
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
@ -31,6 +31,8 @@ public class JwtSecurityContextRepository implements ServerSecurityContextReposi
|
||||
URI requestURI = request.getURI();
|
||||
// 存在jwt时,校验jwt。redis也需要存在
|
||||
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())
|
||||
|
@ -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());
|
||||
|
Loading…
Reference in New Issue
Block a user