diff --git a/maxkey-persistence/src/main/java/org/maxkey/persistence/service/AppsService.java b/maxkey-persistence/src/main/java/org/maxkey/persistence/service/AppsService.java index 8f9a7ee45..7ae37c150 100644 --- a/maxkey-persistence/src/main/java/org/maxkey/persistence/service/AppsService.java +++ b/maxkey-persistence/src/main/java/org/maxkey/persistence/service/AppsService.java @@ -31,7 +31,8 @@ import com.github.benmanes.caffeine.cache.Caffeine; @Repository public class AppsService extends JpaBaseService{ - + + public final static String DETAIL_SUFFIX = "_detail"; protected final static Cache appsDetailsCacheStore = Caffeine.newBuilder() .expireAfterWrite(60, TimeUnit.MINUTES) @@ -63,14 +64,23 @@ public class AppsService extends JpaBaseService{ public List queryMyApps(UserApps userApplications){ return getMapper().queryMyApps(userApplications); } - + //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; + } } diff --git a/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/autoconfigure/Oauth20AutoConfiguration.java b/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/autoconfigure/Oauth20AutoConfiguration.java index a0b50a8e2..e6c965489 100644 --- a/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/autoconfigure/Oauth20AutoConfiguration.java +++ b/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/autoconfigure/Oauth20AutoConfiguration.java @@ -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) { diff --git a/maxkey-webs/maxkey-web-mgt/src/main/java/org/maxkey/MaxKeyMgtConfig.java b/maxkey-webs/maxkey-web-mgt/src/main/java/org/maxkey/MaxKeyMgtConfig.java index d03e22937..635e1a9cd 100644 --- a/maxkey-webs/maxkey-web-mgt/src/main/java/org/maxkey/MaxKeyMgtConfig.java +++ b/maxkey-webs/maxkey-web-mgt/src/main/java/org/maxkey/MaxKeyMgtConfig.java @@ -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( diff --git a/maxkey-webs/maxkey-web-mgt/src/main/java/org/maxkey/Oauth20ClientAutoConfiguration.java b/maxkey-webs/maxkey-web-mgt/src/main/java/org/maxkey/Oauth20ClientAutoConfiguration.java new file mode 100644 index 000000000..d14efbea0 --- /dev/null +++ b/maxkey-webs/maxkey-web-mgt/src/main/java/org/maxkey/Oauth20ClientAutoConfiguration.java @@ -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 { + + } + +} diff --git a/maxkey-webs/maxkey-web-mgt/src/main/java/org/maxkey/web/interceptor/RestApiPermissionAdapter.java b/maxkey-webs/maxkey-web-mgt/src/main/java/org/maxkey/web/interceptor/RestApiPermissionAdapter.java index 8c7baaf8d..f35b87f34 100644 --- a/maxkey-webs/maxkey-web-mgt/src/main/java/org/maxkey/web/interceptor/RestApiPermissionAdapter.java +++ b/maxkey-webs/maxkey-web-mgt/src/main/java/org/maxkey/web/interceptor/RestApiPermissionAdapter.java @@ -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 @@ -46,18 +46,14 @@ import com.github.benmanes.caffeine.cache.Caffeine; @Component public class RestApiPermissionAdapter implements AsyncHandlerInterceptor { private static final Logger _logger = LoggerFactory.getLogger(RestApiPermissionAdapter.class); - - protected static final Cache appsCacheStore = - Caffeine.newBuilder() - .expireAfterWrite(60, TimeUnit.MINUTES) - .build(); - + @Autowired - AppsService appsService; - + @Qualifier("oauth20TokenServices") + DefaultTokenServices oauth20TokenServices; + @Autowired - @Qualifier("passwordReciprocal") - protected PasswordReciprocal passwordReciprocal; + @Qualifier("oauth20ClientAuthenticationManager") + ProviderManager authenticationManager; static ConcurrentHashMapnavigationsMap=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); - } - - _logger.debug("App Info "+ app.getSecret()); - if(app != null && passwordReciprocal.matches(appSecret, app.getSecret())) { - return true; - } + 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 "); + } + } + + 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); diff --git a/maxkey-webs/maxkey-web-mgt/src/main/resources/META-INF/spring.factories b/maxkey-webs/maxkey-web-mgt/src/main/resources/META-INF/spring.factories index e8181dfc0..c83eb1422 100644 --- a/maxkey-webs/maxkey-web-mgt/src/main/resources/META-INF/spring.factories +++ b/maxkey-webs/maxkey-web-mgt/src/main/resources/META-INF/spring.factories @@ -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