From 295cedcaed875555ff2e4dea522b17f3fa320689 Mon Sep 17 00:00:00 2001 From: zhuyijun Date: Sun, 16 Oct 2022 21:19:27 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../security/OauthCsrfTokenRepository.java | 54 +++++++++++++++++++ .../config/security/ResourceServerConfig.java | 6 +-- .../security/WebSecurityConfiguration.java | 1 + .../OauthAuthorizationCodeServices.java | 12 +++-- .../impl/OauthClientDetailsServiceImpl.java | 30 +++++------ .../constant/CommonRedisKeyConstant.java | 4 ++ .../starter/oauth/token/TokenConfig.java | 9 +++- 7 files changed, 91 insertions(+), 25 deletions(-) create mode 100644 server/zyjblogs-oauth/src/main/java/cn/zyjblogs/config/security/OauthCsrfTokenRepository.java diff --git a/server/zyjblogs-oauth/src/main/java/cn/zyjblogs/config/security/OauthCsrfTokenRepository.java b/server/zyjblogs-oauth/src/main/java/cn/zyjblogs/config/security/OauthCsrfTokenRepository.java new file mode 100644 index 0000000..a27e3c8 --- /dev/null +++ b/server/zyjblogs-oauth/src/main/java/cn/zyjblogs/config/security/OauthCsrfTokenRepository.java @@ -0,0 +1,54 @@ +package cn.zyjblogs.config.security; + +import cn.zyjblogs.starter.common.entity.constant.CommonRedisKeyConstant; +import cn.zyjblogs.starter.common.entity.context.BaseContext; +import cn.zyjblogs.starter.redis.utils.RedisTemplateHandler; +import lombok.RequiredArgsConstructor; +import org.springframework.security.web.csrf.CsrfToken; +import org.springframework.security.web.csrf.CsrfTokenRepository; +import org.springframework.security.web.csrf.DefaultCsrfToken; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +@Component +@RequiredArgsConstructor +public class OauthCsrfTokenRepository implements CsrfTokenRepository { + static final String DEFAULT_CSRF_COOKIE_NAME = "XSRF-TOKEN"; + static final String DEFAULT_CSRF_PARAMETER_NAME = "_csrf"; + static final String DEFAULT_CSRF_HEADER_NAME = "X-XSRF-TOKEN"; + private String parameterName = "_csrf"; + private String headerName = "X-XSRF-TOKEN"; + + private final RedisTemplateHandler redisTemplateHandler; + + @Override + public CsrfToken generateToken(HttpServletRequest httpServletRequest) { + return new DefaultCsrfToken(this.headerName, this.parameterName, this.createNewToken()); + } + + @Override + public void saveToken(CsrfToken csrfToken, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) { + String tokenValue = csrfToken == null ? "" : csrfToken.getToken(); + String key = CommonRedisKeyConstant.XSRF_TOKEN + BaseContext.getUsername(); + redisTemplateHandler.set(key, tokenValue); + redisTemplateHandler.expire(key, 30, TimeUnit.DAYS); + httpServletResponse.setHeader(headerName, tokenValue); + } + + @Override + public CsrfToken loadToken(HttpServletRequest httpServletRequest) { + String tokenValue = redisTemplateHandler.get(CommonRedisKeyConstant.XSRF_TOKEN + BaseContext.getUsername()); + if (tokenValue == null) { + return null; + } + return new DefaultCsrfToken(this.headerName, this.parameterName, tokenValue); + } + + private String createNewToken() { + return UUID.randomUUID().toString(); + } +} diff --git a/server/zyjblogs-oauth/src/main/java/cn/zyjblogs/config/security/ResourceServerConfig.java b/server/zyjblogs-oauth/src/main/java/cn/zyjblogs/config/security/ResourceServerConfig.java index f22e4dd..c7d4985 100644 --- a/server/zyjblogs-oauth/src/main/java/cn/zyjblogs/config/security/ResourceServerConfig.java +++ b/server/zyjblogs-oauth/src/main/java/cn/zyjblogs/config/security/ResourceServerConfig.java @@ -26,6 +26,7 @@ public class ResourceServerConfig extends ResourceServerConfigurerAdapter { private String resourceId; private final TokenStore tokenStore; private final WhiteListProperties whiteListProperties; +// private final CsrfTokenRepository oauthCsrfTokenRepository; @Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception { @@ -47,9 +48,8 @@ public class ResourceServerConfig extends ResourceServerConfigurerAdapter { */ @Override public void configure(HttpSecurity http) throws Exception { - http - .csrf() - .disable() + http.csrf().disable() +// .disable() //限制资源服务器作用范围为 "/user/**", "/demo/**" .requestMatchers().antMatchers("/v*/**", "/demo/**", String.join(",", whiteListProperties.getAllowPaths())) diff --git a/server/zyjblogs-oauth/src/main/java/cn/zyjblogs/config/security/WebSecurityConfiguration.java b/server/zyjblogs-oauth/src/main/java/cn/zyjblogs/config/security/WebSecurityConfiguration.java index 57051ae..5809679 100644 --- a/server/zyjblogs-oauth/src/main/java/cn/zyjblogs/config/security/WebSecurityConfiguration.java +++ b/server/zyjblogs-oauth/src/main/java/cn/zyjblogs/config/security/WebSecurityConfiguration.java @@ -20,6 +20,7 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { private final OauthUserDetailsServiceImpl userDetailsService; private final OauthAuthenticationProvider oauthAuthenticationProvider; private final PasswordEncoder passwordEncoder; +// private final CsrfTokenRepository oauthCsrfTokenRepository; /** * 密码编码解码 * diff --git a/server/zyjblogs-oauth/src/main/java/cn/zyjblogs/config/security/policy/OauthAuthorizationCodeServices.java b/server/zyjblogs-oauth/src/main/java/cn/zyjblogs/config/security/policy/OauthAuthorizationCodeServices.java index feb3adf..e99f68a 100644 --- a/server/zyjblogs-oauth/src/main/java/cn/zyjblogs/config/security/policy/OauthAuthorizationCodeServices.java +++ b/server/zyjblogs-oauth/src/main/java/cn/zyjblogs/config/security/policy/OauthAuthorizationCodeServices.java @@ -8,6 +8,8 @@ import org.springframework.security.oauth2.common.util.RandomValueStringGenerato import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices; +import java.util.concurrent.TimeUnit; + /** * @author zhuyijun */ @@ -32,7 +34,9 @@ public class OauthAuthorizationCodeServices implements AuthorizationCodeServices @Override public String createAuthorizationCode(OAuth2Authentication oAuth2Authentication) { String code = this.generator.generate(); - redisTemplateHandler.hPut(CommonRedisKeyConstant.AUTHORIZATION_CODE, code, oAuth2Authentication); + redisTemplateHandler.set(CommonRedisKeyConstant.AUTHORIZATION_CODE + ":" + code, oAuth2Authentication); + redisTemplateHandler.expire(CommonRedisKeyConstant.AUTHORIZATION_CODE + ":" + code, 1, TimeUnit.MINUTES); +// redisTemplateHandler.hPut(CommonRedisKeyConstant.AUTHORIZATION_CODE, code, oAuth2Authentication); return code; } @@ -44,8 +48,10 @@ public class OauthAuthorizationCodeServices implements AuthorizationCodeServices @SneakyThrows @Override public OAuth2Authentication consumeAuthorizationCode(String code) throws InvalidGrantException { - OAuth2Authentication oAuth2Authentication = redisTemplateHandler.hGet(CommonRedisKeyConstant.AUTHORIZATION_CODE, code); - redisTemplateHandler.hDelete(CommonRedisKeyConstant.AUTHORIZATION_CODE, code); +// OAuth2Authentication oAuth2Authentication = redisTemplateHandler.hGet(CommonRedisKeyConstant.AUTHORIZATION_CODE, code); +// redisTemplateHandler.hDelete(CommonRedisKeyConstant.AUTHORIZATION_CODE, code); + OAuth2Authentication oAuth2Authentication = redisTemplateHandler.get(CommonRedisKeyConstant.AUTHORIZATION_CODE + ":" + code); + redisTemplateHandler.delete(CommonRedisKeyConstant.AUTHORIZATION_CODE + ":" + code); return oAuth2Authentication; } diff --git a/server/zyjblogs-oauth/src/main/java/cn/zyjblogs/server/client/service/impl/OauthClientDetailsServiceImpl.java b/server/zyjblogs-oauth/src/main/java/cn/zyjblogs/server/client/service/impl/OauthClientDetailsServiceImpl.java index c49d9c8..d20f01d 100644 --- a/server/zyjblogs-oauth/src/main/java/cn/zyjblogs/server/client/service/impl/OauthClientDetailsServiceImpl.java +++ b/server/zyjblogs-oauth/src/main/java/cn/zyjblogs/server/client/service/impl/OauthClientDetailsServiceImpl.java @@ -5,16 +5,10 @@ import cn.zyjblogs.server.client.po.OauthClientDetail; import cn.zyjblogs.starter.common.utils.bean.BeanUtils; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; - import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.RequiredArgsConstructor; import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.oauth2.provider.ClientAlreadyExistsException; -import org.springframework.security.oauth2.provider.ClientDetails; -import org.springframework.security.oauth2.provider.ClientDetailsService; -import org.springframework.security.oauth2.provider.ClientRegistrationException; -import org.springframework.security.oauth2.provider.ClientRegistrationService; -import org.springframework.security.oauth2.provider.NoSuchClientException; +import org.springframework.security.oauth2.provider.*; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; @@ -31,16 +25,16 @@ public class OauthClientDetailsServiceImpl implements ClientDetailsService, Clie private final PasswordEncoder passwordEncoder; @Override - public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException { - LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); - wrapper.eq(OauthClientDetail::getClientId,clientId); + public OauthClientDetail loadClientByClientId(String clientId) throws ClientRegistrationException { + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); + wrapper.eq(OauthClientDetail::getClientId, clientId); return oauthClientDetailsMapper.selectOne(wrapper); } @Override public void addClientDetails(ClientDetails clientDetails) throws ClientAlreadyExistsException { OauthClientDetail oauthClientDetail = new OauthClientDetail(clientDetails); - if (oauthClientDetail.getClientSecret() != null){ + if (oauthClientDetail.getClientSecret() != null) { oauthClientDetail.setClientSecret(passwordEncoder.encode(oauthClientDetail.getClientSecret())); } oauthClientDetailsMapper.insert(oauthClientDetail); @@ -49,31 +43,31 @@ public class OauthClientDetailsServiceImpl implements ClientDetailsService, Clie @Override public void updateClientDetails(ClientDetails clientDetails) throws NoSuchClientException { LambdaUpdateWrapper updateWrapper = Wrappers.lambdaUpdate(); - updateWrapper.eq(OauthClientDetail::getClientId,clientDetails.getClientId()); + updateWrapper.eq(OauthClientDetail::getClientId, clientDetails.getClientId()); OauthClientDetail oauthClientDetail = new OauthClientDetail(clientDetails); - oauthClientDetailsMapper.update(oauthClientDetail,updateWrapper); + oauthClientDetailsMapper.update(oauthClientDetail, updateWrapper); } @Override public void updateClientSecret(String clientId, String clientSecret) throws NoSuchClientException { LambdaUpdateWrapper updateWrapper = Wrappers.lambdaUpdate(); - updateWrapper.eq(OauthClientDetail::getClientId,clientId).set(OauthClientDetail::getClientSecret,passwordEncoder.encode(clientSecret)); - oauthClientDetailsMapper.update(null,updateWrapper); + updateWrapper.eq(OauthClientDetail::getClientId, clientId).set(OauthClientDetail::getClientSecret, passwordEncoder.encode(clientSecret)); + oauthClientDetailsMapper.update(null, updateWrapper); } @Override public void removeClientDetails(String clientId) throws NoSuchClientException { LambdaUpdateWrapper updateWrapper = Wrappers.lambdaUpdate(); - updateWrapper.eq(OauthClientDetail::getClientId,clientId); + updateWrapper.eq(OauthClientDetail::getClientId, clientId); oauthClientDetailsMapper.delete(updateWrapper); } @Override public List listClientDetails() { List oauthClientDetails = oauthClientDetailsMapper.selectList(Wrappers.emptyWrapper()); - if (CollectionUtils.isEmpty(oauthClientDetails)){ + if (CollectionUtils.isEmpty(oauthClientDetails)) { return new ArrayList<>(); } - return BeanUtils.map(oauthClientDetails,ClientDetails.class); + return BeanUtils.map(oauthClientDetails, ClientDetails.class); } } diff --git a/stater/zyjblogs-common-spring-boot-starter/src/main/java/cn/zyjblogs/starter/common/entity/constant/CommonRedisKeyConstant.java b/stater/zyjblogs-common-spring-boot-starter/src/main/java/cn/zyjblogs/starter/common/entity/constant/CommonRedisKeyConstant.java index da8439b..1ff2d7e 100644 --- a/stater/zyjblogs-common-spring-boot-starter/src/main/java/cn/zyjblogs/starter/common/entity/constant/CommonRedisKeyConstant.java +++ b/stater/zyjblogs-common-spring-boot-starter/src/main/java/cn/zyjblogs/starter/common/entity/constant/CommonRedisKeyConstant.java @@ -13,6 +13,10 @@ public class CommonRedisKeyConstant { */ public static final String REDIS_KEY_PRIVATE_RSA = "rsa:key:private_key"; public static final String REDIS_KEY_PUBLIC_RSA = "rsa:key:public_key"; + /** + * csrf防护key + */ + public static final String XSRF_TOKEN = "XSRF_TOKEN:"; private CommonRedisKeyConstant() { } diff --git a/stater/zyjblogs-oauth-spring-boot-starter/src/main/java/cn/zyjblogs/starter/oauth/token/TokenConfig.java b/stater/zyjblogs-oauth-spring-boot-starter/src/main/java/cn/zyjblogs/starter/oauth/token/TokenConfig.java index a42846b..37e8293 100644 --- a/stater/zyjblogs-oauth-spring-boot-starter/src/main/java/cn/zyjblogs/starter/oauth/token/TokenConfig.java +++ b/stater/zyjblogs-oauth-spring-boot-starter/src/main/java/cn/zyjblogs/starter/oauth/token/TokenConfig.java @@ -1,6 +1,8 @@ package cn.zyjblogs.starter.oauth.token; import cn.zyjblogs.starter.common.autoconfigure.rsa.RsaKeyProperties; +import cn.zyjblogs.starter.common.entity.constant.CommonRedisKeyConstant; +import cn.zyjblogs.starter.redis.utils.RedisTemplateHandler; import lombok.RequiredArgsConstructor; import org.apache.commons.io.IOUtils; import org.springframework.context.annotation.Bean; @@ -10,6 +12,7 @@ import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; import org.springframework.security.oauth2.provider.token.store.JwtTokenStore; +import org.springframework.util.StringUtils; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -24,6 +27,7 @@ public class TokenConfig { private final RsaKeyProperties rsaKeyProperties; private final OauthAccessTokenConverter oauthAccessTokenConverter; + private final RedisTemplateHandler redisTemplateHandler; @Bean public PasswordEncoder passwordEncoder() { @@ -46,7 +50,10 @@ public class TokenConfig { JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); try { if (rsaKeyProperties.getEnable()) { - String publicKey = IOUtils.toString(Paths.get(rsaKeyProperties.getPubKeyPath()).toUri(), StandardCharsets.UTF_8); + String publicKey = redisTemplateHandler.get(CommonRedisKeyConstant.REDIS_KEY_PUBLIC_RSA); + if (!StringUtils.hasLength(publicKey)) { + publicKey = IOUtils.toString(Paths.get(rsaKeyProperties.getPubKeyPath()).toUri(), StandardCharsets.UTF_8); + } // 公钥验签 converter.setVerifierKey(publicKey); }