oauth client_credentials Optimize

This commit is contained in:
MaxKey 2021-11-14 08:25:18 +08:00
parent 26de7922e5
commit cf5aaf251a
6 changed files with 187 additions and 95 deletions

View File

@ -32,6 +32,7 @@ import com.github.benmanes.caffeine.cache.Caffeine;
@Repository
public class AppsService extends JpaBaseService<Apps>{
public final static String DETAIL_SUFFIX = "_detail";
protected final static Cache<String, Apps> appsDetailsCacheStore =
Caffeine.newBuilder()
.expireAfterWrite(60, TimeUnit.MINUTES)
@ -66,11 +67,20 @@ public class AppsService extends JpaBaseService<Apps>{
//cache for running
public void storeCacheAppDetails(String appId, Apps appDetails) {
appsDetailsCacheStore.put(appId, appDetails);
appsDetailsCacheStore.put(appId + DETAIL_SUFFIX, appDetails);
}
public Apps getCacheAppDetails(String appId) {
Apps appDetails=appsDetailsCacheStore.getIfPresent(appId);
Apps appDetails=appsDetailsCacheStore.getIfPresent(appId + DETAIL_SUFFIX);
return appDetails;
}
public Apps loadAppById(String id) {
Apps app = appsDetailsCacheStore.getIfPresent(id);
if(app == null) {
app = get(id);
appsDetailsCacheStore.put(id, app);
}
return app;
}
}

View File

@ -223,7 +223,7 @@ public class Oauth20AutoConfiguration implements InitializingBean {
* @return oauth20JdbcClientDetailsService
*/
@Bean(name = "oauth20JdbcClientDetailsService")
public JdbcClientDetailsService clientDetailsService(DataSource dataSource,PasswordEncoder passwordReciprocal) {
public JdbcClientDetailsService jdbcClientDetailsService(DataSource dataSource,PasswordEncoder passwordReciprocal) {
JdbcClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
clientDetailsService.setPasswordEncoder(passwordReciprocal);
_logger.debug("OAuth 2 Jdbc ClientDetails Service init.");
@ -235,7 +235,7 @@ public class Oauth20AutoConfiguration implements InitializingBean {
* @return oauth20TokenServices
*/
@Bean(name = "oauth20TokenServices")
public DefaultTokenServices DefaultTokenServices(
public DefaultTokenServices defaultTokenServices(
JdbcClientDetailsService oauth20JdbcClientDetailsService,
TokenStore oauth20TokenStore,
OIDCIdTokenEnhancer tokenEnhancer) {

View File

@ -17,25 +17,17 @@
package org.maxkey;
import javax.sql.DataSource;
import org.maxkey.authz.oauth2.provider.client.JdbcClientDetailsService;
import org.maxkey.authz.oauth2.provider.token.DefaultTokenServices;
import org.maxkey.authz.oauth2.provider.token.TokenStore;
import org.maxkey.authz.oauth2.provider.token.store.InMemoryTokenStore;
import org.maxkey.authz.oauth2.provider.token.store.RedisTokenStore;
import org.maxkey.password.onetimepwd.AbstractOtpAuthn;
import org.maxkey.password.onetimepwd.impl.TimeBasedOtpAuthn;
import org.maxkey.persistence.db.LoginHistoryService;
import org.maxkey.persistence.db.LoginService;
import org.maxkey.persistence.db.PasswordPolicyValidator;
import org.maxkey.persistence.redis.RedisConnectionFactory;
import org.maxkey.persistence.service.UserInfoService;
import org.maxkey.authn.realm.jdbc.JdbcAuthenticationRealm;
import org.maxkey.authn.support.rememberme.AbstractRemeberMeService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
@ -45,54 +37,6 @@ import org.springframework.security.crypto.password.PasswordEncoder;
public class MaxKeyMgtConfig implements InitializingBean {
private static final Logger _logger = LoggerFactory.getLogger(MaxKeyMgtConfig.class);
@Bean(name = "oauth20JdbcClientDetailsService")
public JdbcClientDetailsService JdbcClientDetailsService(
DataSource dataSource,PasswordEncoder passwordReciprocal) {
JdbcClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
clientDetailsService.setPasswordEncoder(passwordReciprocal);
_logger.debug("JdbcClientDetailsService inited.");
return clientDetailsService;
}
/**
* TokenStore.
* @param persistence int
* @return oauth20TokenStore
*/
@Bean(name = "oauth20TokenStore")
public TokenStore oauth20TokenStore(
@Value("${maxkey.server.persistence}") int persistence,
JdbcTemplate jdbcTemplate,
RedisConnectionFactory jedisConnectionFactory) {
TokenStore tokenStore = null;
if (persistence == 2) {
tokenStore = new RedisTokenStore(jedisConnectionFactory);
_logger.debug("RedisTokenStore");
}else {
tokenStore = new InMemoryTokenStore();
_logger.debug("InMemoryTokenStore");
}
return tokenStore;
}
/**
* clientDetailsUserDetailsService.
* @return oauth20TokenServices
*/
@Bean(name = "oauth20TokenServices")
public DefaultTokenServices DefaultTokenServices(
JdbcClientDetailsService oauth20JdbcClientDetailsService,
TokenStore oauth20TokenStore) {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setClientDetailsService(oauth20JdbcClientDetailsService);
tokenServices.setTokenStore(oauth20TokenStore);
tokenServices.setSupportRefreshToken(true);
return tokenServices;
}
//authenticationRealm for MaxKeyMgtApplication
@Bean(name = "authenticationRealm")
public JdbcAuthenticationRealm authenticationRealm(

View File

@ -0,0 +1,121 @@
/*
* Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.maxkey;
import javax.sql.DataSource;
import org.maxkey.authz.oauth2.provider.client.ClientDetailsUserDetailsService;
import org.maxkey.authz.oauth2.provider.client.JdbcClientDetailsService;
import org.maxkey.authz.oauth2.provider.token.DefaultTokenServices;
import org.maxkey.authz.oauth2.provider.token.TokenStore;
import org.maxkey.authz.oauth2.provider.token.store.InMemoryTokenStore;
import org.maxkey.authz.oauth2.provider.token.store.RedisTokenStore;
import org.maxkey.persistence.redis.RedisConnectionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* like Oauth20AutoConfiguration for mgmt
* @author Crystal.Sea
*
*/
@Configuration
public class Oauth20ClientAutoConfiguration implements InitializingBean {
private static final Logger _logger = LoggerFactory.getLogger(Oauth20ClientAutoConfiguration.class);
@Bean(name = "oauth20JdbcClientDetailsService")
public JdbcClientDetailsService jdbcClientDetailsService(
DataSource dataSource,PasswordEncoder passwordReciprocal) {
JdbcClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
clientDetailsService.setPasswordEncoder(passwordReciprocal);
_logger.debug("JdbcClientDetailsService inited.");
return clientDetailsService;
}
/**
* TokenStore.
* @param persistence int
* @return oauth20TokenStore
*/
@Bean(name = "oauth20TokenStore")
public TokenStore oauth20TokenStore(
@Value("${maxkey.server.persistence}") int persistence,
JdbcTemplate jdbcTemplate,
RedisConnectionFactory jedisConnectionFactory) {
TokenStore tokenStore = null;
if (persistence == 2) {
tokenStore = new RedisTokenStore(jedisConnectionFactory);
_logger.debug("RedisTokenStore");
}else {
tokenStore = new InMemoryTokenStore();
_logger.debug("InMemoryTokenStore");
}
return tokenStore;
}
/**
* clientDetailsUserDetailsService.
* @return oauth20TokenServices
*/
@Bean(name = "oauth20TokenServices")
public DefaultTokenServices defaultTokenServices(
JdbcClientDetailsService oauth20JdbcClientDetailsService,
TokenStore oauth20TokenStore) {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setClientDetailsService(oauth20JdbcClientDetailsService);
tokenServices.setTokenStore(oauth20TokenStore);
tokenServices.setSupportRefreshToken(true);
return tokenServices;
}
/**
* ProviderManager.
* @return oauth20ClientAuthenticationManager
*/
@Bean(name = "oauth20ClientAuthenticationManager")
public ProviderManager oauth20ClientAuthenticationManager(
JdbcClientDetailsService oauth20JdbcClientDetailsService,
PasswordEncoder passwordReciprocal
) {
ClientDetailsUserDetailsService cientDetailsUserDetailsService =
new ClientDetailsUserDetailsService(oauth20JdbcClientDetailsService);
DaoAuthenticationProvider daoAuthenticationProvider= new DaoAuthenticationProvider();
daoAuthenticationProvider.setPasswordEncoder(passwordReciprocal);
daoAuthenticationProvider.setUserDetailsService(cientDetailsUserDetailsService);
ProviderManager authenticationManager = new ProviderManager(daoAuthenticationProvider);
_logger.debug("OAuth 2 Client Authentication Manager init.");
return authenticationManager;
}
@Override
public void afterPropertiesSet() throws Exception {
}
}

View File

@ -18,26 +18,26 @@
package org.maxkey.web.interceptor;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.maxkey.crypto.password.PasswordReciprocal;
import org.maxkey.entity.apps.Apps;
import org.maxkey.persistence.service.AppsService;
import org.maxkey.authz.oauth2.provider.OAuth2Authentication;
import org.maxkey.authz.oauth2.provider.token.DefaultTokenServices;
import org.maxkey.util.AuthorizationHeaderCredential;
import org.maxkey.util.AuthorizationHeaderUtils;
import org.maxkey.util.StringUtils;
import org.maxkey.web.WebContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
/**
* basic认证Interceptor处理.
* @author Crystal.Sea
@ -47,17 +47,13 @@ import com.github.benmanes.caffeine.cache.Caffeine;
public class RestApiPermissionAdapter implements AsyncHandlerInterceptor {
private static final Logger _logger = LoggerFactory.getLogger(RestApiPermissionAdapter.class);
protected static final Cache<String, Apps> appsCacheStore =
Caffeine.newBuilder()
.expireAfterWrite(60, TimeUnit.MINUTES)
.build();
@Autowired
@Qualifier("oauth20TokenServices")
DefaultTokenServices oauth20TokenServices;
@Autowired
AppsService appsService;
@Autowired
@Qualifier("passwordReciprocal")
protected PasswordReciprocal passwordReciprocal;
@Qualifier("oauth20ClientAuthenticationManager")
ProviderManager authenticationManager;
static ConcurrentHashMap<String ,String >navigationsMap=null;
@ -70,27 +66,47 @@ public class RestApiPermissionAdapter implements AsyncHandlerInterceptor {
public boolean preHandle(HttpServletRequest request,HttpServletResponse response, Object handler) throws Exception {
_logger.trace("RestApiPermissionAdapter preHandle");
String authorization = request.getHeader(AuthorizationHeaderUtils.AUTHORIZATION_HEADERNAME);
AuthorizationHeaderCredential headerCredential = AuthorizationHeaderUtils.resolve(authorization);
//判断应用的AppId和Secret
if(headerCredential != null){
String appId = headerCredential.getUsername();
String appSecret = headerCredential.getCredential();
_logger.trace("appId "+ appId+" , appSecret " + appSecret);
Apps app = appsCacheStore.getIfPresent(appId);
if (app == null) {
app = appsService.get(appId);
appsCacheStore.put(appId, app);
UsernamePasswordAuthenticationToken authenticationToken = null;
if(headerCredential.getCredentialType().equals(AuthorizationHeaderCredential.Credential.BASIC)) {
if(StringUtils.isNotBlank(headerCredential.getUsername())&&
StringUtils.isNotBlank(headerCredential.getCredential())
) {
UsernamePasswordAuthenticationToken authRequest =
new UsernamePasswordAuthenticationToken(
headerCredential.getUsername(),
headerCredential.getCredential());
authenticationToken= (UsernamePasswordAuthenticationToken)authenticationManager.authenticate(authRequest);
}
}else {
_logger.trace("Authentication bearer " + headerCredential.getCredential());
OAuth2Authentication oauth2Authentication =
oauth20TokenServices.loadAuthentication(headerCredential.getCredential());
if(oauth2Authentication != null) {
_logger.trace("Authentication token " + oauth2Authentication.getPrincipal().toString());
authenticationToken= new UsernamePasswordAuthenticationToken(
new User(
oauth2Authentication.getPrincipal().toString(),
"CLIENT_SECRET",
oauth2Authentication.getAuthorities()),
"PASSWORD",
oauth2Authentication.getAuthorities()
);
}else {
_logger.trace("Authentication token is null ");
}
}
_logger.debug("App Info "+ app.getSecret());
if(app != null && passwordReciprocal.matches(appSecret, app.getSecret())) {
if(authenticationToken !=null && authenticationToken.isAuthenticated()) {
WebContext.setAuthentication(authenticationToken);
return true;
}
}
_logger.trace("No Authentication ... forward to /login");
RequestDispatcher dispatcher = request.getRequestDispatcher("/login");
dispatcher.forward(request, response);

View File

@ -8,6 +8,7 @@ org.maxkey.autoconfigure.RedisAutoConfiguration,\
org.maxkey.autoconfigure.AuthenticationAutoConfiguration,\
org.maxkey.synchronizer.autoconfigure.SynchronizerAutoConfiguration,\
org.maxkey.autoconfigure.SwaggerConfig,\
org.maxkey.Oauth20ClientAutoConfiguration,\
org.maxkey.MaxKeyMgtConfig,\
org.maxkey.MaxKeyMgtJobs,\
org.maxkey.MaxKeyMgtMvcConfig