浏览代码

oauth client_credentials Optimize

MaxKey 3 年之前
父节点
当前提交
cf5aaf251a

+ 14 - 4
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<Apps>{
-
+	
+	public final static String DETAIL_SUFFIX	=	"_detail";
 	protected final static  Cache<String, Apps> appsDetailsCacheStore = 
 			Caffeine.newBuilder()
                 .expireAfterWrite(60, TimeUnit.MINUTES)
@@ -63,14 +64,23 @@ public class AppsService extends JpaBaseService<Apps>{
     public List<UserApps> 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;
+    }
 }

+ 2 - 2
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) {

+ 0 - 56
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(

+ 121 - 0
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 {
+        
+    }
+
+}

+ 49 - 33
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<String, Apps> 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  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);
-		    }
-		    
-		    _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);

+ 1 - 0
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