mirror of
https://gitee.com/dromara/MaxKey.git
synced 2025-12-07 17:38:32 +08:00
oauth client_credentials Optimize
This commit is contained in:
parent
26de7922e5
commit
cf5aaf251a
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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 {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user