diff --git a/docs/authn/social.md b/docs/authn/social.md index 5947f4e01..125993c33 100644 --- a/docs/authn/social.md +++ b/docs/authn/social.md @@ -40,7 +40,7 @@ config.socialsignon.sinaweibo.account.id=id config.socialsignon.sinaweibo.sortorder=1 -配置maxkey/spring/maxkey-support-social.xml +配置maxkey/spring/maxkey.xml

 <bean id="socialSignOnWeibo" class="org.maxkey.authn.support.socialsignon.service.SocialSignOnProvider">
diff --git a/maxkey-protocols/maxkey-protocol-cas/src/main/java/org/maxkey/authz/cas/endpoint/ticket/service/JdbcTicketServices.java b/maxkey-protocols/maxkey-protocol-cas/src/main/java/org/maxkey/authz/cas/endpoint/ticket/service/JdbcTicketServices.java
index 94f52cc34..8c113efe1 100644
--- a/maxkey-protocols/maxkey-protocol-cas/src/main/java/org/maxkey/authz/cas/endpoint/ticket/service/JdbcTicketServices.java
+++ b/maxkey-protocols/maxkey-protocol-cas/src/main/java/org/maxkey/authz/cas/endpoint/ticket/service/JdbcTicketServices.java
@@ -3,9 +3,6 @@ package org.maxkey.authz.cas.endpoint.ticket.service;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Types;
-
-import javax.sql.DataSource;
-
 import org.maxkey.authz.cas.endpoint.ticket.Ticket;
 import org.maxkey.util.SerializationUtils;
 import org.springframework.dao.EmptyResultDataAccessException;
@@ -27,9 +24,9 @@ public class JdbcTicketServices extends RandomServiceTicketServices {
 
 	private final JdbcTemplate jdbcTemplate;
 
-	public JdbcTicketServices(DataSource dataSource) {
-		Assert.notNull(dataSource, "DataSource required");
-		this.jdbcTemplate = new JdbcTemplate(dataSource);
+	public JdbcTicketServices(JdbcTemplate jdbcTemplate) {
+		Assert.notNull(jdbcTemplate, "jdbcTemplate required");
+		this.jdbcTemplate = jdbcTemplate;
 	}
 
 	@Override
diff --git a/maxkey-protocols/maxkey-protocol-formbased/src/main/java/org/maxkey/authz/formbased/endpoint/FormBasedAuthorizeEndpoint.java b/maxkey-protocols/maxkey-protocol-formbased/src/main/java/org/maxkey/authz/formbased/endpoint/FormBasedAuthorizeEndpoint.java
index 71c2ba31b..1f21aa577 100644
--- a/maxkey-protocols/maxkey-protocol-formbased/src/main/java/org/maxkey/authz/formbased/endpoint/FormBasedAuthorizeEndpoint.java
+++ b/maxkey-protocols/maxkey-protocol-formbased/src/main/java/org/maxkey/authz/formbased/endpoint/FormBasedAuthorizeEndpoint.java
@@ -73,7 +73,7 @@ public class FormBasedAuthorizeEndpoint  extends AuthorizeBaseEndpoint{
 					modelAndView);
 		}
 		
-		
+		_logger.debug("FormBased View Name " + modelAndView.getViewName());
 		
 		return modelAndView;
 	}
diff --git a/maxkey-protocols/maxkey-protocol-formbased/src/main/java/org/maxkey/authz/formbased/endpoint/adapter/FormBasedNetease163EmailAdapter.java b/maxkey-protocols/maxkey-protocol-formbased/src/main/java/org/maxkey/authz/formbased/endpoint/adapter/FormBasedNetease163EmailAdapter.java
index 4db5bcfd6..2052b0232 100644
--- a/maxkey-protocols/maxkey-protocol-formbased/src/main/java/org/maxkey/authz/formbased/endpoint/adapter/FormBasedNetease163EmailAdapter.java
+++ b/maxkey-protocols/maxkey-protocol-formbased/src/main/java/org/maxkey/authz/formbased/endpoint/adapter/FormBasedNetease163EmailAdapter.java
@@ -21,7 +21,7 @@ public class FormBasedNetease163EmailAdapter extends AbstractAuthorizeAdapter {
 
 	@Override
 	public ModelAndView authorize(UserInfo userInfo, Object app, String data,ModelAndView modelAndView) {
-		modelAndView.setViewName("authorize/formbased_netease_163email_sso_submint");
+		modelAndView.setViewName("authorize/formbased_163email_sso_submint");
 		AppsFormBasedDetails details=(AppsFormBasedDetails)app;
 		modelAndView.addObject("username", details.getAppUser().getRelatedUsername().substring(details.getAppUser().getRelatedUsername().indexOf("@")));
 		modelAndView.addObject("email", details.getAppUser().getRelatedUsername());
diff --git a/maxkey-protocols/maxkey-protocol-formbased/src/main/java/org/maxkey/authz/formbased/endpoint/adapter/FormBasedNeteaseNoteYoudaoAdapter.java b/maxkey-protocols/maxkey-protocol-formbased/src/main/java/org/maxkey/authz/formbased/endpoint/adapter/FormBasedNeteaseNoteYoudaoAdapter.java
index dd2dbeebe..0d51aa3df 100644
--- a/maxkey-protocols/maxkey-protocol-formbased/src/main/java/org/maxkey/authz/formbased/endpoint/adapter/FormBasedNeteaseNoteYoudaoAdapter.java
+++ b/maxkey-protocols/maxkey-protocol-formbased/src/main/java/org/maxkey/authz/formbased/endpoint/adapter/FormBasedNeteaseNoteYoudaoAdapter.java
@@ -23,7 +23,7 @@ public class FormBasedNeteaseNoteYoudaoAdapter extends AbstractAuthorizeAdapter
 
 	@Override
 	public ModelAndView authorize(UserInfo userInfo, Object app, String data,ModelAndView modelAndView) {
-		modelAndView.setViewName("authorize/formbased_netease_noteyoudao_sso_submint");
+		modelAndView.setViewName("authorize/formbased_wy_youdao_sso_submint");
 		AppsFormBasedDetails details=(AppsFormBasedDetails)app;
 		modelAndView.addObject("username", details.getAppUser().getRelatedUsername());
 		modelAndView.addObject("password",  DigestUtils.md5Hex(details.getAppUser().getRelatedPassword()));
diff --git a/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/code/JdbcAuthorizationCodeServices.java b/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/code/JdbcAuthorizationCodeServices.java
index 676657d5e..cdfcc867f 100644
--- a/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/code/JdbcAuthorizationCodeServices.java
+++ b/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/code/JdbcAuthorizationCodeServices.java
@@ -15,65 +15,70 @@ import org.springframework.jdbc.core.support.SqlLobValue;
 import org.springframework.util.Assert;
 
 /**
- * Implementation of authorization code services that stores the codes and authentication in a database.
+ * Implementation of authorization code services that stores the codes and
+ * authentication in a database.
  * 
  * @author Crystal.Sea
  */
 public class JdbcAuthorizationCodeServices extends RandomValueAuthorizationCodeServices {
 
-	private static final String DEFAULT_SELECT_STATEMENT = "select code, authentication from oauth_code where code = ?";
-	private static final String DEFAULT_INSERT_STATEMENT = "insert into oauth_code (code, authentication) values (?, ?)";
-	private static final String DEFAULT_DELETE_STATEMENT = "delete from oauth_code where code = ?";
+    private static final String DEFAULT_SELECT_STATEMENT = "select code, authentication from oauth_code where code = ?";
+    private static final String DEFAULT_INSERT_STATEMENT = "insert into oauth_code (code, authentication) values (?, ?)";
+    private static final String DEFAULT_DELETE_STATEMENT = "delete from oauth_code where code = ?";
 
-	private String selectAuthenticationSql = DEFAULT_SELECT_STATEMENT;
-	private String insertAuthenticationSql = DEFAULT_INSERT_STATEMENT;
-	private String deleteAuthenticationSql = DEFAULT_DELETE_STATEMENT;
+    private String selectAuthenticationSql = DEFAULT_SELECT_STATEMENT;
+    private String insertAuthenticationSql = DEFAULT_INSERT_STATEMENT;
+    private String deleteAuthenticationSql = DEFAULT_DELETE_STATEMENT;
 
-	private final JdbcTemplate jdbcTemplate;
+    private final JdbcTemplate jdbcTemplate;
 
-	public JdbcAuthorizationCodeServices(DataSource dataSource) {
-		Assert.notNull(dataSource, "DataSource required");
-		this.jdbcTemplate = new JdbcTemplate(dataSource);
-	}
+    public JdbcAuthorizationCodeServices(DataSource dataSource) {
+        Assert.notNull(dataSource, "DataSource required");
+        this.jdbcTemplate = new JdbcTemplate(dataSource);
+    }
 
-	@Override
-	protected void store(String code, OAuth2Authentication authentication) {
-		jdbcTemplate.update(insertAuthenticationSql,
-				new Object[] { code, new SqlLobValue(SerializationUtils.serialize(authentication)) }, new int[] {
-						Types.VARCHAR, Types.BLOB });
-	}
+    public JdbcAuthorizationCodeServices(JdbcTemplate jdbcTemplate) {
+        Assert.notNull(jdbcTemplate, "jdbcTemplate required");
+        this.jdbcTemplate = jdbcTemplate;
+    }
+    
+    @Override
+    protected void store(String code, OAuth2Authentication authentication) {
+        jdbcTemplate.update(insertAuthenticationSql,
+                new Object[] { code, new SqlLobValue(SerializationUtils.serialize(authentication)) },
+                new int[] { Types.VARCHAR, Types.BLOB });
+    }
 
-	public OAuth2Authentication remove(String code) {
-		OAuth2Authentication authentication;
+    public OAuth2Authentication remove(String code) {
+        OAuth2Authentication authentication;
 
-		try {
-			authentication = jdbcTemplate.queryForObject(selectAuthenticationSql,
-					new RowMapper() {
-						public OAuth2Authentication mapRow(ResultSet rs, int rowNum)
-								throws SQLException {
-							return SerializationUtils.deserialize(rs.getBytes("authentication"));
-						}
-					}, code);
-		} catch (EmptyResultDataAccessException e) {
-			return null;
-		}
+        try {
+            authentication = jdbcTemplate.queryForObject(selectAuthenticationSql,
+                    new RowMapper() {
+                        public OAuth2Authentication mapRow(ResultSet rs, int rowNum) throws SQLException {
+                            return SerializationUtils.deserialize(rs.getBytes("authentication"));
+                        }
+                    }, code);
+        } catch (EmptyResultDataAccessException e) {
+            return null;
+        }
 
-		if (authentication != null) {
-			jdbcTemplate.update(deleteAuthenticationSql, code);
-		}
+        if (authentication != null) {
+            jdbcTemplate.update(deleteAuthenticationSql, code);
+        }
 
-		return authentication;
-	}
+        return authentication;
+    }
 
-	public void setSelectAuthenticationSql(String selectAuthenticationSql) {
-		this.selectAuthenticationSql = selectAuthenticationSql;
-	}
+    public void setSelectAuthenticationSql(String selectAuthenticationSql) {
+        this.selectAuthenticationSql = selectAuthenticationSql;
+    }
 
-	public void setInsertAuthenticationSql(String insertAuthenticationSql) {
-		this.insertAuthenticationSql = insertAuthenticationSql;
-	}
+    public void setInsertAuthenticationSql(String insertAuthenticationSql) {
+        this.insertAuthenticationSql = insertAuthenticationSql;
+    }
 
-	public void setDeleteAuthenticationSql(String deleteAuthenticationSql) {
-		this.deleteAuthenticationSql = deleteAuthenticationSql;
-	}
+    public void setDeleteAuthenticationSql(String deleteAuthenticationSql) {
+        this.deleteAuthenticationSql = deleteAuthenticationSql;
+    }
 }
diff --git a/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/token/store/JdbcTokenStore.java b/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/token/store/JdbcTokenStore.java
index 667b90318..6b1bbbd40 100644
--- a/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/token/store/JdbcTokenStore.java
+++ b/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/token/store/JdbcTokenStore.java
@@ -37,423 +37,416 @@ import org.springframework.util.Assert;
  */
 public class JdbcTokenStore implements TokenStore {
 
-	private static final Log LOG = LogFactory.getLog(JdbcTokenStore.class);
+    private static final Log LOG = LogFactory.getLog(JdbcTokenStore.class);
 
-	private static final String DEFAULT_ACCESS_TOKEN_INSERT_STATEMENT = "insert into oauth_access_token (token_id, token, authentication_id, user_name, client_id, authentication, refresh_token) values (?, ?, ?, ?, ?, ?, ?)";
+    private static final String DEFAULT_ACCESS_TOKEN_INSERT_STATEMENT = "insert into oauth_access_token (token_id, token, authentication_id, user_name, client_id, authentication, refresh_token) values (?, ?, ?, ?, ?, ?, ?)";
 
-	private static final String DEFAULT_ACCESS_TOKEN_SELECT_STATEMENT = "select token_id, token from oauth_access_token where token_id = ?";
+    private static final String DEFAULT_ACCESS_TOKEN_SELECT_STATEMENT = "select token_id, token from oauth_access_token where token_id = ?";
 
-	private static final String DEFAULT_ACCESS_TOKEN_AUTHENTICATION_SELECT_STATEMENT = "select token_id, authentication from oauth_access_token where token_id = ?";
+    private static final String DEFAULT_ACCESS_TOKEN_AUTHENTICATION_SELECT_STATEMENT = "select token_id, authentication from oauth_access_token where token_id = ?";
 
-	private static final String DEFAULT_ACCESS_TOKEN_FROM_AUTHENTICATION_SELECT_STATEMENT = "select token_id, token from oauth_access_token where authentication_id = ?";
+    private static final String DEFAULT_ACCESS_TOKEN_FROM_AUTHENTICATION_SELECT_STATEMENT = "select token_id, token from oauth_access_token where authentication_id = ?";
 
-	private static final String DEFAULT_ACCESS_TOKENS_FROM_USERNAME_AND_CLIENT_SELECT_STATEMENT = "select token_id, token from oauth_access_token where user_name = ? and client_id = ?";
+    private static final String DEFAULT_ACCESS_TOKENS_FROM_USERNAME_AND_CLIENT_SELECT_STATEMENT = "select token_id, token from oauth_access_token where user_name = ? and client_id = ?";
 
-	private static final String DEFAULT_ACCESS_TOKENS_FROM_USERNAME_SELECT_STATEMENT = "select token_id, token from oauth_access_token where user_name = ?";
-	
-	private static final String DEFAULT_ACCESS_TOKENS_FROM_CLIENTID_SELECT_STATEMENT = "select token_id, token from oauth_access_token where client_id = ?";
+    private static final String DEFAULT_ACCESS_TOKENS_FROM_USERNAME_SELECT_STATEMENT = "select token_id, token from oauth_access_token where user_name = ?";
 
-	private static final String DEFAULT_ACCESS_TOKEN_DELETE_STATEMENT = "delete from oauth_access_token where token_id = ?";
+    private static final String DEFAULT_ACCESS_TOKENS_FROM_CLIENTID_SELECT_STATEMENT = "select token_id, token from oauth_access_token where client_id = ?";
 
-	private static final String DEFAULT_ACCESS_TOKEN_DELETE_FROM_REFRESH_TOKEN_STATEMENT = "delete from oauth_access_token where refresh_token = ?";
+    private static final String DEFAULT_ACCESS_TOKEN_DELETE_STATEMENT = "delete from oauth_access_token where token_id = ?";
 
-	private static final String DEFAULT_REFRESH_TOKEN_INSERT_STATEMENT = "insert into oauth_refresh_token (token_id, token, authentication) values (?, ?, ?)";
+    private static final String DEFAULT_ACCESS_TOKEN_DELETE_FROM_REFRESH_TOKEN_STATEMENT = "delete from oauth_access_token where refresh_token = ?";
 
-	private static final String DEFAULT_REFRESH_TOKEN_SELECT_STATEMENT = "select token_id, token from oauth_refresh_token where token_id = ?";
+    private static final String DEFAULT_REFRESH_TOKEN_INSERT_STATEMENT = "insert into oauth_refresh_token (token_id, token, authentication) values (?, ?, ?)";
 
-	private static final String DEFAULT_REFRESH_TOKEN_AUTHENTICATION_SELECT_STATEMENT = "select token_id, authentication from oauth_refresh_token where token_id = ?";
+    private static final String DEFAULT_REFRESH_TOKEN_SELECT_STATEMENT = "select token_id, token from oauth_refresh_token where token_id = ?";
 
-	private static final String DEFAULT_REFRESH_TOKEN_DELETE_STATEMENT = "delete from oauth_refresh_token where token_id = ?";
+    private static final String DEFAULT_REFRESH_TOKEN_AUTHENTICATION_SELECT_STATEMENT = "select token_id, authentication from oauth_refresh_token where token_id = ?";
 
-	private String insertAccessTokenSql = DEFAULT_ACCESS_TOKEN_INSERT_STATEMENT;
+    private static final String DEFAULT_REFRESH_TOKEN_DELETE_STATEMENT = "delete from oauth_refresh_token where token_id = ?";
 
-	private String selectAccessTokenSql = DEFAULT_ACCESS_TOKEN_SELECT_STATEMENT;
+    private String insertAccessTokenSql = DEFAULT_ACCESS_TOKEN_INSERT_STATEMENT;
 
-	private String selectAccessTokenAuthenticationSql = DEFAULT_ACCESS_TOKEN_AUTHENTICATION_SELECT_STATEMENT;
+    private String selectAccessTokenSql = DEFAULT_ACCESS_TOKEN_SELECT_STATEMENT;
 
-	private String selectAccessTokenFromAuthenticationSql = DEFAULT_ACCESS_TOKEN_FROM_AUTHENTICATION_SELECT_STATEMENT;
+    private String selectAccessTokenAuthenticationSql = DEFAULT_ACCESS_TOKEN_AUTHENTICATION_SELECT_STATEMENT;
 
-	private String selectAccessTokensFromUserNameAndClientIdSql = DEFAULT_ACCESS_TOKENS_FROM_USERNAME_AND_CLIENT_SELECT_STATEMENT;
+    private String selectAccessTokenFromAuthenticationSql = DEFAULT_ACCESS_TOKEN_FROM_AUTHENTICATION_SELECT_STATEMENT;
 
-	private String selectAccessTokensFromUserNameSql = DEFAULT_ACCESS_TOKENS_FROM_USERNAME_SELECT_STATEMENT;
-	
-	private String selectAccessTokensFromClientIdSql = DEFAULT_ACCESS_TOKENS_FROM_CLIENTID_SELECT_STATEMENT;
+    private String selectAccessTokensFromUserNameAndClientIdSql = DEFAULT_ACCESS_TOKENS_FROM_USERNAME_AND_CLIENT_SELECT_STATEMENT;
 
-	private String deleteAccessTokenSql = DEFAULT_ACCESS_TOKEN_DELETE_STATEMENT;
+    private String selectAccessTokensFromUserNameSql = DEFAULT_ACCESS_TOKENS_FROM_USERNAME_SELECT_STATEMENT;
 
-	private String insertRefreshTokenSql = DEFAULT_REFRESH_TOKEN_INSERT_STATEMENT;
+    private String selectAccessTokensFromClientIdSql = DEFAULT_ACCESS_TOKENS_FROM_CLIENTID_SELECT_STATEMENT;
 
-	private String selectRefreshTokenSql = DEFAULT_REFRESH_TOKEN_SELECT_STATEMENT;
+    private String deleteAccessTokenSql = DEFAULT_ACCESS_TOKEN_DELETE_STATEMENT;
 
-	private String selectRefreshTokenAuthenticationSql = DEFAULT_REFRESH_TOKEN_AUTHENTICATION_SELECT_STATEMENT;
+    private String insertRefreshTokenSql = DEFAULT_REFRESH_TOKEN_INSERT_STATEMENT;
 
-	private String deleteRefreshTokenSql = DEFAULT_REFRESH_TOKEN_DELETE_STATEMENT;
+    private String selectRefreshTokenSql = DEFAULT_REFRESH_TOKEN_SELECT_STATEMENT;
 
-	private String deleteAccessTokenFromRefreshTokenSql = DEFAULT_ACCESS_TOKEN_DELETE_FROM_REFRESH_TOKEN_STATEMENT;
+    private String selectRefreshTokenAuthenticationSql = DEFAULT_REFRESH_TOKEN_AUTHENTICATION_SELECT_STATEMENT;
 
-	private AuthenticationKeyGenerator authenticationKeyGenerator = new DefaultAuthenticationKeyGenerator();
+    private String deleteRefreshTokenSql = DEFAULT_REFRESH_TOKEN_DELETE_STATEMENT;
 
-	private final JdbcTemplate jdbcTemplate;
+    private String deleteAccessTokenFromRefreshTokenSql = DEFAULT_ACCESS_TOKEN_DELETE_FROM_REFRESH_TOKEN_STATEMENT;
 
-	public JdbcTokenStore(DataSource dataSource) {
-		Assert.notNull(dataSource, "DataSource required");
-		this.jdbcTemplate = new JdbcTemplate(dataSource);
-	}
+    private AuthenticationKeyGenerator authenticationKeyGenerator = new DefaultAuthenticationKeyGenerator();
 
-	public void setAuthenticationKeyGenerator(AuthenticationKeyGenerator authenticationKeyGenerator) {
-		this.authenticationKeyGenerator = authenticationKeyGenerator;
-	}
+    private final JdbcTemplate jdbcTemplate;
 
-	public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) {
-		OAuth2AccessToken accessToken = null;
-
-		String key = authenticationKeyGenerator.extractKey(authentication);
-		try {
-			accessToken = jdbcTemplate.queryForObject(selectAccessTokenFromAuthenticationSql,
-					new RowMapper() {
-						public OAuth2AccessToken mapRow(ResultSet rs, int rowNum) throws SQLException {
-							return deserializeAccessToken(rs.getBytes(2));
-						}
-					}, key);
-		}
-		catch (EmptyResultDataAccessException e) {
-			if (LOG.isInfoEnabled()) {
-				LOG.debug("Failed to find access token for authentication " + authentication);
-			}
-		}
-		catch (IllegalArgumentException e) {
-			LOG.error("Could not extract access token for authentication " + authentication, e);
-		}
-
-		if (accessToken != null
-				&& !key.equals(authenticationKeyGenerator.extractKey(readAuthentication(accessToken.getValue())))) {
-			removeAccessToken(accessToken.getValue());
-			// Keep the store consistent (maybe the same user is represented by this authentication but the details have
-			// changed)
-			storeAccessToken(accessToken, authentication);
-		}
-		return accessToken;
-	}
-
-	public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
-		String refreshToken = null;
-		if (token.getRefreshToken() != null) {
-			refreshToken = token.getRefreshToken().getValue();
-		}
-		
-		if (readAccessToken(token.getValue())!=null) {
-			removeAccessToken(token.getValue());
-		}
-
-		jdbcTemplate.update(insertAccessTokenSql, new Object[] { extractTokenKey(token.getValue()),
-				new SqlLobValue(serializeAccessToken(token)), authenticationKeyGenerator.extractKey(authentication),
-				authentication.isClientOnly() ? null : authentication.getName(),
-				authentication.getOAuth2Request().getClientId(),
-				new SqlLobValue(serializeAuthentication(authentication)), extractTokenKey(refreshToken) }, new int[] {
-				Types.VARCHAR, Types.BLOB, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.BLOB, Types.VARCHAR });
-	}
-
-	public OAuth2AccessToken readAccessToken(String tokenValue) {
-		OAuth2AccessToken accessToken = null;
-
-		try {
-			accessToken = jdbcTemplate.queryForObject(selectAccessTokenSql, new RowMapper() {
-				public OAuth2AccessToken mapRow(ResultSet rs, int rowNum) throws SQLException {
-					return deserializeAccessToken(rs.getBytes(2));
-				}
-			}, extractTokenKey(tokenValue));
-		}
-		catch (EmptyResultDataAccessException e) {
-			if (LOG.isInfoEnabled()) {
-				LOG.info("Failed to find access token for token " + tokenValue);
-			}
-		}
-		catch (IllegalArgumentException e) {
-			LOG.warn("Failed to deserialize access token for " + tokenValue, e);
-			removeAccessToken(tokenValue);
-		}
-
-		return accessToken;
-	}
-
-	public void removeAccessToken(OAuth2AccessToken token) {
-		removeAccessToken(token.getValue());
-	}
-
-	public void removeAccessToken(String tokenValue) {
-		jdbcTemplate.update(deleteAccessTokenSql, extractTokenKey(tokenValue));
-	}
-
-	public OAuth2Authentication readAuthentication(OAuth2AccessToken token) {
-		return readAuthentication(token.getValue());
-	}
-
-	public OAuth2Authentication readAuthentication(String token) {
-		OAuth2Authentication authentication = null;
-
-		try {
-			authentication = jdbcTemplate.queryForObject(selectAccessTokenAuthenticationSql,
-					new RowMapper() {
-						public OAuth2Authentication mapRow(ResultSet rs, int rowNum) throws SQLException {
-							return deserializeAuthentication(rs.getBytes(2));
-						}
-					}, extractTokenKey(token));
-		}
-		catch (EmptyResultDataAccessException e) {
-			if (LOG.isInfoEnabled()) {
-				LOG.info("Failed to find access token for token " + token);
-			}
-		}
-		catch (IllegalArgumentException e) {
-			LOG.warn("Failed to deserialize authentication for " + token, e);
-			removeAccessToken(token);
-		}
-
-		return authentication;
-	}
-
-	public void storeRefreshToken(OAuth2RefreshToken refreshToken, OAuth2Authentication authentication) {
-		jdbcTemplate.update(insertRefreshTokenSql, new Object[] { extractTokenKey(refreshToken.getValue()),
-				new SqlLobValue(serializeRefreshToken(refreshToken)),
-				new SqlLobValue(serializeAuthentication(authentication)) }, new int[] { Types.VARCHAR, Types.BLOB,
-				Types.BLOB });
-	}
-
-	public OAuth2RefreshToken readRefreshToken(String token) {
-		OAuth2RefreshToken refreshToken = null;
-
-		try {
-			refreshToken = jdbcTemplate.queryForObject(selectRefreshTokenSql, new RowMapper() {
-				public OAuth2RefreshToken mapRow(ResultSet rs, int rowNum) throws SQLException {
-					return deserializeRefreshToken(rs.getBytes(2));
-				}
-			}, extractTokenKey(token));
-		}
-		catch (EmptyResultDataAccessException e) {
-			if (LOG.isInfoEnabled()) {
-				LOG.info("Failed to find refresh token for token " + token);
-			}
-		}
-		catch (IllegalArgumentException e) {
-			LOG.warn("Failed to deserialize refresh token for token " + token, e);
-			removeRefreshToken(token);
-		}
-
-		return refreshToken;
-	}
-
-	public void removeRefreshToken(OAuth2RefreshToken token) {
-		removeRefreshToken(token.getValue());
-	}
-
-	public void removeRefreshToken(String token) {
-		jdbcTemplate.update(deleteRefreshTokenSql, extractTokenKey(token));
-	}
-
-	public OAuth2Authentication readAuthenticationForRefreshToken(OAuth2RefreshToken token) {
-		return readAuthenticationForRefreshToken(token.getValue());
-	}
-
-	public OAuth2Authentication readAuthenticationForRefreshToken(String value) {
-		OAuth2Authentication authentication = null;
-
-		try {
-			authentication = jdbcTemplate.queryForObject(selectRefreshTokenAuthenticationSql,
-					new RowMapper() {
-						public OAuth2Authentication mapRow(ResultSet rs, int rowNum) throws SQLException {
-							return deserializeAuthentication(rs.getBytes(2));
-						}
-					}, extractTokenKey(value));
-		}
-		catch (EmptyResultDataAccessException e) {
-			if (LOG.isInfoEnabled()) {
-				LOG.info("Failed to find access token for token " + value);
-			}
-		}
-		catch (IllegalArgumentException e) {
-			LOG.warn("Failed to deserialize access token for " + value, e);
-			removeRefreshToken(value);
-		}
-
-		return authentication;
-	}
-
-	public void removeAccessTokenUsingRefreshToken(OAuth2RefreshToken refreshToken) {
-		removeAccessTokenUsingRefreshToken(refreshToken.getValue());
-	}
-
-	public void removeAccessTokenUsingRefreshToken(String refreshToken) {
-		jdbcTemplate.update(deleteAccessTokenFromRefreshTokenSql, new Object[] { extractTokenKey(refreshToken) },
-				new int[] { Types.VARCHAR });
-	}
-
-	public Collection findTokensByClientId(String clientId) {
-		List accessTokens = new ArrayList();
-
-		try {
-			accessTokens = jdbcTemplate.query(selectAccessTokensFromClientIdSql, new SafeAccessTokenRowMapper(),
-					clientId);
-		}
-		catch (EmptyResultDataAccessException e) {
-			if (LOG.isInfoEnabled()) {
-				LOG.info("Failed to find access token for clientId " + clientId);
-			}
-		}
-		accessTokens = removeNulls(accessTokens);
-
-		return accessTokens;
-	}
-
-	public Collection findTokensByUserName(String userName) {
-			List accessTokens = new ArrayList();
-	
-			try {
-				accessTokens = jdbcTemplate.query(selectAccessTokensFromUserNameSql, new SafeAccessTokenRowMapper(),
-						userName);
-			}
-			catch (EmptyResultDataAccessException e) {
-				if (LOG.isInfoEnabled())
-					LOG.info("Failed to find access token for userName " + userName);
-			}
-			accessTokens = removeNulls(accessTokens);
-	
-			return accessTokens;
-		}
-		
-		public Collection findTokensByClientIdAndUserName(String clientId, String userName) {
-		List accessTokens = new ArrayList();
-
-		try {
-			accessTokens = jdbcTemplate.query(selectAccessTokensFromUserNameAndClientIdSql, new SafeAccessTokenRowMapper(),
-					userName, clientId);
-		}
-		catch (EmptyResultDataAccessException e) {
-			if (LOG.isInfoEnabled()) {
-				LOG.info("Failed to find access token for clientId " + clientId + " and userName " + userName);
-			}
-		}
-		accessTokens = removeNulls(accessTokens);
-
-		return accessTokens;
-	}
-
-	private List removeNulls(List accessTokens) {
-		List tokens = new ArrayList();
-		for (OAuth2AccessToken token : accessTokens) {
-			if (token != null) {
-				tokens.add(token);
-			}
-		}
-		return tokens;
-	}
-
-	protected String extractTokenKey(String value) {
-		if (value == null) {
-			return null;
-		}
-		MessageDigest digest;
-		try {
-			digest = MessageDigest.getInstance("MD5");
-		}
-		catch (NoSuchAlgorithmException e) {
-			throw new IllegalStateException("MD5 algorithm not available.  Fatal (should be in the JDK).");
-		}
-
-		try {
-			byte[] bytes = digest.digest(value.getBytes("UTF-8"));
-			return String.format("%032x", new BigInteger(1, bytes));
-		}
-		catch (UnsupportedEncodingException e) {
-			throw new IllegalStateException("UTF-8 encoding not available.  Fatal (should be in the JDK).");
-		}
-	}
-
-	private final class SafeAccessTokenRowMapper implements RowMapper {
-		public OAuth2AccessToken mapRow(ResultSet rs, int rowNum) throws SQLException {
-			try {
-				return deserializeAccessToken(rs.getBytes(2));
-			}
-			catch (IllegalArgumentException e) {
-				String token = rs.getString(1);
-				jdbcTemplate.update(deleteAccessTokenSql, token);
-				return null;
-			}
-		}
-	}
-
-	protected byte[] serializeAccessToken(OAuth2AccessToken token) {
-		return SerializationUtils.serialize(token);
-	}
-
-	protected byte[] serializeRefreshToken(OAuth2RefreshToken token) {
-		return SerializationUtils.serialize(token);
-	}
-
-	protected byte[] serializeAuthentication(OAuth2Authentication authentication) {
-		return SerializationUtils.serialize(authentication);
-	}
-
-	protected OAuth2AccessToken deserializeAccessToken(byte[] token) {
-		return SerializationUtils.deserialize(token);
-	}
-
-	protected OAuth2RefreshToken deserializeRefreshToken(byte[] token) {
-		return SerializationUtils.deserialize(token);
-	}
-
-	protected OAuth2Authentication deserializeAuthentication(byte[] authentication) {
-		return SerializationUtils.deserialize(authentication);
-	}
-
-	public void setInsertAccessTokenSql(String insertAccessTokenSql) {
-		this.insertAccessTokenSql = insertAccessTokenSql;
-	}
-
-	public void setSelectAccessTokenSql(String selectAccessTokenSql) {
-		this.selectAccessTokenSql = selectAccessTokenSql;
-	}
-
-	public void setDeleteAccessTokenSql(String deleteAccessTokenSql) {
-		this.deleteAccessTokenSql = deleteAccessTokenSql;
-	}
-
-	public void setInsertRefreshTokenSql(String insertRefreshTokenSql) {
-		this.insertRefreshTokenSql = insertRefreshTokenSql;
-	}
-
-	public void setSelectRefreshTokenSql(String selectRefreshTokenSql) {
-		this.selectRefreshTokenSql = selectRefreshTokenSql;
-	}
-
-	public void setDeleteRefreshTokenSql(String deleteRefreshTokenSql) {
-		this.deleteRefreshTokenSql = deleteRefreshTokenSql;
-	}
-
-	public void setSelectAccessTokenAuthenticationSql(String selectAccessTokenAuthenticationSql) {
-		this.selectAccessTokenAuthenticationSql = selectAccessTokenAuthenticationSql;
-	}
-
-	public void setSelectRefreshTokenAuthenticationSql(String selectRefreshTokenAuthenticationSql) {
-		this.selectRefreshTokenAuthenticationSql = selectRefreshTokenAuthenticationSql;
-	}
-
-	public void setSelectAccessTokenFromAuthenticationSql(String selectAccessTokenFromAuthenticationSql) {
-		this.selectAccessTokenFromAuthenticationSql = selectAccessTokenFromAuthenticationSql;
-	}
-
-	public void setDeleteAccessTokenFromRefreshTokenSql(String deleteAccessTokenFromRefreshTokenSql) {
-		this.deleteAccessTokenFromRefreshTokenSql = deleteAccessTokenFromRefreshTokenSql;
-	}
-  
-	public void setSelectAccessTokensFromUserNameSql(String selectAccessTokensFromUserNameSql) {
-	  		this.selectAccessTokensFromUserNameSql = selectAccessTokensFromUserNameSql;
-	}
-	  
-  	public void setSelectAccessTokensFromUserNameAndClientIdSql(String selectAccessTokensFromUserNameAndClientIdSql) {
-  		this.selectAccessTokensFromUserNameAndClientIdSql = selectAccessTokensFromUserNameAndClientIdSql;
-  	}
-
-	public void setSelectAccessTokensFromClientIdSql(String selectAccessTokensFromClientIdSql) {
-		this.selectAccessTokensFromClientIdSql = selectAccessTokensFromClientIdSql;
-	}
+    public JdbcTokenStore(DataSource dataSource) {
+        Assert.notNull(dataSource, "DataSource required");
+        this.jdbcTemplate = new JdbcTemplate(dataSource);
+    }
+
+    public JdbcTokenStore(JdbcTemplate jdbcTemplate) {
+        Assert.notNull(jdbcTemplate, "jdbcTemplate required");
+        this.jdbcTemplate = jdbcTemplate;
+    }
+
+    public void setAuthenticationKeyGenerator(AuthenticationKeyGenerator authenticationKeyGenerator) {
+        this.authenticationKeyGenerator = authenticationKeyGenerator;
+    }
+
+    public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) {
+        OAuth2AccessToken accessToken = null;
+
+        String key = authenticationKeyGenerator.extractKey(authentication);
+        try {
+            accessToken = jdbcTemplate.queryForObject(selectAccessTokenFromAuthenticationSql,
+                    new RowMapper() {
+                        public OAuth2AccessToken mapRow(ResultSet rs, int rowNum) throws SQLException {
+                            return deserializeAccessToken(rs.getBytes(2));
+                        }
+                    }, key);
+        } catch (EmptyResultDataAccessException e) {
+            if (LOG.isInfoEnabled()) {
+                LOG.debug("Failed to find access token for authentication " + authentication);
+            }
+        } catch (IllegalArgumentException e) {
+            LOG.error("Could not extract access token for authentication " + authentication, e);
+        }
+
+        if (accessToken != null
+                && !key.equals(authenticationKeyGenerator.extractKey(readAuthentication(accessToken.getValue())))) {
+            removeAccessToken(accessToken.getValue());
+            // Keep the store consistent (maybe the same user is represented by this
+            // authentication but the details have
+            // changed)
+            storeAccessToken(accessToken, authentication);
+        }
+        return accessToken;
+    }
+
+    public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
+        String refreshToken = null;
+        if (token.getRefreshToken() != null) {
+            refreshToken = token.getRefreshToken().getValue();
+        }
+
+        if (readAccessToken(token.getValue()) != null) {
+            removeAccessToken(token.getValue());
+        }
+
+        jdbcTemplate.update(insertAccessTokenSql,
+                new Object[] { extractTokenKey(token.getValue()), new SqlLobValue(serializeAccessToken(token)),
+                        authenticationKeyGenerator.extractKey(authentication),
+                        authentication.isClientOnly() ? null : authentication.getName(),
+                        authentication.getOAuth2Request().getClientId(),
+                        new SqlLobValue(serializeAuthentication(authentication)), extractTokenKey(refreshToken) },
+                new int[] { Types.VARCHAR, Types.BLOB, Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.BLOB,
+                        Types.VARCHAR });
+    }
+
+    public OAuth2AccessToken readAccessToken(String tokenValue) {
+        OAuth2AccessToken accessToken = null;
+
+        try {
+            accessToken = jdbcTemplate.queryForObject(selectAccessTokenSql, new RowMapper() {
+                public OAuth2AccessToken mapRow(ResultSet rs, int rowNum) throws SQLException {
+                    return deserializeAccessToken(rs.getBytes(2));
+                }
+            }, extractTokenKey(tokenValue));
+        } catch (EmptyResultDataAccessException e) {
+            if (LOG.isInfoEnabled()) {
+                LOG.info("Failed to find access token for token " + tokenValue);
+            }
+        } catch (IllegalArgumentException e) {
+            LOG.warn("Failed to deserialize access token for " + tokenValue, e);
+            removeAccessToken(tokenValue);
+        }
+
+        return accessToken;
+    }
+
+    public void removeAccessToken(OAuth2AccessToken token) {
+        removeAccessToken(token.getValue());
+    }
+
+    public void removeAccessToken(String tokenValue) {
+        jdbcTemplate.update(deleteAccessTokenSql, extractTokenKey(tokenValue));
+    }
+
+    public OAuth2Authentication readAuthentication(OAuth2AccessToken token) {
+        return readAuthentication(token.getValue());
+    }
+
+    public OAuth2Authentication readAuthentication(String token) {
+        OAuth2Authentication authentication = null;
+
+        try {
+            authentication = jdbcTemplate.queryForObject(selectAccessTokenAuthenticationSql,
+                    new RowMapper() {
+                        public OAuth2Authentication mapRow(ResultSet rs, int rowNum) throws SQLException {
+                            return deserializeAuthentication(rs.getBytes(2));
+                        }
+                    }, extractTokenKey(token));
+        } catch (EmptyResultDataAccessException e) {
+            if (LOG.isInfoEnabled()) {
+                LOG.info("Failed to find access token for token " + token);
+            }
+        } catch (IllegalArgumentException e) {
+            LOG.warn("Failed to deserialize authentication for " + token, e);
+            removeAccessToken(token);
+        }
+
+        return authentication;
+    }
+
+    public void storeRefreshToken(OAuth2RefreshToken refreshToken, OAuth2Authentication authentication) {
+        jdbcTemplate.update(insertRefreshTokenSql,
+                new Object[] { extractTokenKey(refreshToken.getValue()),
+                        new SqlLobValue(serializeRefreshToken(refreshToken)),
+                        new SqlLobValue(serializeAuthentication(authentication)) },
+                new int[] { Types.VARCHAR, Types.BLOB, Types.BLOB });
+    }
+
+    public OAuth2RefreshToken readRefreshToken(String token) {
+        OAuth2RefreshToken refreshToken = null;
+
+        try {
+            refreshToken = jdbcTemplate.queryForObject(selectRefreshTokenSql, new RowMapper() {
+                public OAuth2RefreshToken mapRow(ResultSet rs, int rowNum) throws SQLException {
+                    return deserializeRefreshToken(rs.getBytes(2));
+                }
+            }, extractTokenKey(token));
+        } catch (EmptyResultDataAccessException e) {
+            if (LOG.isInfoEnabled()) {
+                LOG.info("Failed to find refresh token for token " + token);
+            }
+        } catch (IllegalArgumentException e) {
+            LOG.warn("Failed to deserialize refresh token for token " + token, e);
+            removeRefreshToken(token);
+        }
+
+        return refreshToken;
+    }
+
+    public void removeRefreshToken(OAuth2RefreshToken token) {
+        removeRefreshToken(token.getValue());
+    }
+
+    public void removeRefreshToken(String token) {
+        jdbcTemplate.update(deleteRefreshTokenSql, extractTokenKey(token));
+    }
+
+    public OAuth2Authentication readAuthenticationForRefreshToken(OAuth2RefreshToken token) {
+        return readAuthenticationForRefreshToken(token.getValue());
+    }
+
+    public OAuth2Authentication readAuthenticationForRefreshToken(String value) {
+        OAuth2Authentication authentication = null;
+
+        try {
+            authentication = jdbcTemplate.queryForObject(selectRefreshTokenAuthenticationSql,
+                    new RowMapper() {
+                        public OAuth2Authentication mapRow(ResultSet rs, int rowNum) throws SQLException {
+                            return deserializeAuthentication(rs.getBytes(2));
+                        }
+                    }, extractTokenKey(value));
+        } catch (EmptyResultDataAccessException e) {
+            if (LOG.isInfoEnabled()) {
+                LOG.info("Failed to find access token for token " + value);
+            }
+        } catch (IllegalArgumentException e) {
+            LOG.warn("Failed to deserialize access token for " + value, e);
+            removeRefreshToken(value);
+        }
+
+        return authentication;
+    }
+
+    public void removeAccessTokenUsingRefreshToken(OAuth2RefreshToken refreshToken) {
+        removeAccessTokenUsingRefreshToken(refreshToken.getValue());
+    }
+
+    public void removeAccessTokenUsingRefreshToken(String refreshToken) {
+        jdbcTemplate.update(deleteAccessTokenFromRefreshTokenSql, new Object[] { extractTokenKey(refreshToken) },
+                new int[] { Types.VARCHAR });
+    }
+
+    public Collection findTokensByClientId(String clientId) {
+        List accessTokens = new ArrayList();
+
+        try {
+            accessTokens = jdbcTemplate.query(selectAccessTokensFromClientIdSql, new SafeAccessTokenRowMapper(),
+                    clientId);
+        } catch (EmptyResultDataAccessException e) {
+            if (LOG.isInfoEnabled()) {
+                LOG.info("Failed to find access token for clientId " + clientId);
+            }
+        }
+        accessTokens = removeNulls(accessTokens);
+
+        return accessTokens;
+    }
+
+    public Collection findTokensByUserName(String userName) {
+        List accessTokens = new ArrayList();
+
+        try {
+            accessTokens = jdbcTemplate.query(selectAccessTokensFromUserNameSql, new SafeAccessTokenRowMapper(),
+                    userName);
+        } catch (EmptyResultDataAccessException e) {
+            if (LOG.isInfoEnabled())
+                LOG.info("Failed to find access token for userName " + userName);
+        }
+        accessTokens = removeNulls(accessTokens);
+
+        return accessTokens;
+    }
+
+    public Collection findTokensByClientIdAndUserName(String clientId, String userName) {
+        List accessTokens = new ArrayList();
+
+        try {
+            accessTokens = jdbcTemplate.query(selectAccessTokensFromUserNameAndClientIdSql,
+                    new SafeAccessTokenRowMapper(), userName, clientId);
+        } catch (EmptyResultDataAccessException e) {
+            if (LOG.isInfoEnabled()) {
+                LOG.info("Failed to find access token for clientId " + clientId + " and userName " + userName);
+            }
+        }
+        accessTokens = removeNulls(accessTokens);
+
+        return accessTokens;
+    }
+
+    private List removeNulls(List accessTokens) {
+        List tokens = new ArrayList();
+        for (OAuth2AccessToken token : accessTokens) {
+            if (token != null) {
+                tokens.add(token);
+            }
+        }
+        return tokens;
+    }
+
+    protected String extractTokenKey(String value) {
+        if (value == null) {
+            return null;
+        }
+        MessageDigest digest;
+        try {
+            digest = MessageDigest.getInstance("MD5");
+        } catch (NoSuchAlgorithmException e) {
+            throw new IllegalStateException("MD5 algorithm not available.  Fatal (should be in the JDK).");
+        }
+
+        try {
+            byte[] bytes = digest.digest(value.getBytes("UTF-8"));
+            return String.format("%032x", new BigInteger(1, bytes));
+        } catch (UnsupportedEncodingException e) {
+            throw new IllegalStateException("UTF-8 encoding not available.  Fatal (should be in the JDK).");
+        }
+    }
+
+    private final class SafeAccessTokenRowMapper implements RowMapper {
+        public OAuth2AccessToken mapRow(ResultSet rs, int rowNum) throws SQLException {
+            try {
+                return deserializeAccessToken(rs.getBytes(2));
+            } catch (IllegalArgumentException e) {
+                String token = rs.getString(1);
+                jdbcTemplate.update(deleteAccessTokenSql, token);
+                return null;
+            }
+        }
+    }
+
+    protected byte[] serializeAccessToken(OAuth2AccessToken token) {
+        return SerializationUtils.serialize(token);
+    }
+
+    protected byte[] serializeRefreshToken(OAuth2RefreshToken token) {
+        return SerializationUtils.serialize(token);
+    }
+
+    protected byte[] serializeAuthentication(OAuth2Authentication authentication) {
+        return SerializationUtils.serialize(authentication);
+    }
+
+    protected OAuth2AccessToken deserializeAccessToken(byte[] token) {
+        return SerializationUtils.deserialize(token);
+    }
+
+    protected OAuth2RefreshToken deserializeRefreshToken(byte[] token) {
+        return SerializationUtils.deserialize(token);
+    }
+
+    protected OAuth2Authentication deserializeAuthentication(byte[] authentication) {
+        return SerializationUtils.deserialize(authentication);
+    }
+
+    public void setInsertAccessTokenSql(String insertAccessTokenSql) {
+        this.insertAccessTokenSql = insertAccessTokenSql;
+    }
+
+    public void setSelectAccessTokenSql(String selectAccessTokenSql) {
+        this.selectAccessTokenSql = selectAccessTokenSql;
+    }
+
+    public void setDeleteAccessTokenSql(String deleteAccessTokenSql) {
+        this.deleteAccessTokenSql = deleteAccessTokenSql;
+    }
+
+    public void setInsertRefreshTokenSql(String insertRefreshTokenSql) {
+        this.insertRefreshTokenSql = insertRefreshTokenSql;
+    }
+
+    public void setSelectRefreshTokenSql(String selectRefreshTokenSql) {
+        this.selectRefreshTokenSql = selectRefreshTokenSql;
+    }
+
+    public void setDeleteRefreshTokenSql(String deleteRefreshTokenSql) {
+        this.deleteRefreshTokenSql = deleteRefreshTokenSql;
+    }
+
+    public void setSelectAccessTokenAuthenticationSql(String selectAccessTokenAuthenticationSql) {
+        this.selectAccessTokenAuthenticationSql = selectAccessTokenAuthenticationSql;
+    }
+
+    public void setSelectRefreshTokenAuthenticationSql(String selectRefreshTokenAuthenticationSql) {
+        this.selectRefreshTokenAuthenticationSql = selectRefreshTokenAuthenticationSql;
+    }
+
+    public void setSelectAccessTokenFromAuthenticationSql(String selectAccessTokenFromAuthenticationSql) {
+        this.selectAccessTokenFromAuthenticationSql = selectAccessTokenFromAuthenticationSql;
+    }
+
+    public void setDeleteAccessTokenFromRefreshTokenSql(String deleteAccessTokenFromRefreshTokenSql) {
+        this.deleteAccessTokenFromRefreshTokenSql = deleteAccessTokenFromRefreshTokenSql;
+    }
+
+    public void setSelectAccessTokensFromUserNameSql(String selectAccessTokensFromUserNameSql) {
+        this.selectAccessTokensFromUserNameSql = selectAccessTokensFromUserNameSql;
+    }
+
+    public void setSelectAccessTokensFromUserNameAndClientIdSql(String selectAccessTokensFromUserNameAndClientIdSql) {
+        this.selectAccessTokensFromUserNameAndClientIdSql = selectAccessTokensFromUserNameAndClientIdSql;
+    }
+
+    public void setSelectAccessTokensFromClientIdSql(String selectAccessTokensFromClientIdSql) {
+        this.selectAccessTokensFromClientIdSql = selectAccessTokensFromClientIdSql;
+    }
 }
diff --git a/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/token/store/JwtAccessTokenConverter.java b/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/token/store/JwtAccessTokenConverter.java
index 8b746bc30..ded111dd5 100644
--- a/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/token/store/JwtAccessTokenConverter.java
+++ b/maxkey-protocols/maxkey-protocol-oauth-2.0/src/main/java/org/maxkey/authz/oauth2/provider/token/store/JwtAccessTokenConverter.java
@@ -49,8 +49,9 @@ import org.springframework.security.crypto.codec.Base64;
 import org.springframework.util.Assert;
 
 /**
- * Helper that translates between JWT encoded token values and OAuth authentication information (in both directions).
- * Also acts as a {@link TokenEnhancer} when tokens are granted.
+ * Helper that translates between JWT encoded token values and OAuth
+ * authentication information (in both directions). Also acts as a
+ * {@link TokenEnhancer} when tokens are granted.
  * 
  * @see TokenEnhancer
  * @see AccessTokenConverter
@@ -60,219 +61,218 @@ import org.springframework.util.Assert;
  */
 public class JwtAccessTokenConverter implements TokenEnhancer, AccessTokenConverter, InitializingBean {
 
-	/**
-	 * Field name for token id.
-	 */
-	public static final String TOKEN_ID = AccessTokenConverter.JTI;
+    /**
+     * Field name for token id.
+     */
+    public static final String TOKEN_ID = AccessTokenConverter.JTI;
 
-	private static final Log logger = LogFactory.getLog(JwtAccessTokenConverter.class);
+    private static final Log logger = LogFactory.getLog(JwtAccessTokenConverter.class);
 
-	private AccessTokenConverter tokenConverter = new DefaultAccessTokenConverter();
+    private AccessTokenConverter tokenConverter = new DefaultAccessTokenConverter();
 
-	private JsonParser objectMapper = JsonParserFactory.create();
+    private JsonParser objectMapper = JsonParserFactory.create();
 
-	private String verifierKey = new RandomValueStringGenerator().generate();
+    private String verifierKey = new RandomValueStringGenerator().generate();
 
-	private Signer signer = new MacSigner(verifierKey);
+    private Signer signer = new MacSigner(verifierKey);
 
-	private String signingKey = verifierKey;
+    private String signingKey = verifierKey;
 
-	private SignatureVerifier verifier;
+    private SignatureVerifier verifier;
 
-	/**
-	 * @param tokenConverter the tokenConverter to set
-	 */
-	public void setAccessTokenConverter(AccessTokenConverter tokenConverter) {
-		this.tokenConverter = tokenConverter;
-	}
+    /**
+     * @param tokenConverter the tokenConverter to set
+     */
+    public void setAccessTokenConverter(AccessTokenConverter tokenConverter) {
+        this.tokenConverter = tokenConverter;
+    }
 
-	/**
-	 * @return the tokenConverter in use
-	 */
-	public AccessTokenConverter getAccessTokenConverter() {
-		return tokenConverter;
-	}
+    /**
+     * @return the tokenConverter in use
+     */
+    public AccessTokenConverter getAccessTokenConverter() {
+        return tokenConverter;
+    }
 
-	@Override
-	public Map convertAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
-		return tokenConverter.convertAccessToken(token, authentication);
-	}
+    @Override
+    public Map convertAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
+        return tokenConverter.convertAccessToken(token, authentication);
+    }
 
-	@Override
-	public OAuth2AccessToken extractAccessToken(String value, Map map) {
-		return tokenConverter.extractAccessToken(value, map);
-	}
+    @Override
+    public OAuth2AccessToken extractAccessToken(String value, Map map) {
+        return tokenConverter.extractAccessToken(value, map);
+    }
 
-	@Override
-	public OAuth2Authentication extractAuthentication(Map map) {
-		return tokenConverter.extractAuthentication(map);
-	}
+    @Override
+    public OAuth2Authentication extractAuthentication(Map map) {
+        return tokenConverter.extractAuthentication(map);
+    }
 
-	/**
-	 * Get the verification key for the token signatures.
-	 * 
-	 * @return the key used to verify tokens
-	 */
-	public Map getKey() {
-		Map result = new LinkedHashMap();
-		result.put("alg", signer.algorithm());
-		result.put("value", verifierKey);
-		return result;
-	}
+    /**
+     * Get the verification key for the token signatures.
+     * 
+     * @return the key used to verify tokens
+     */
+    public Map getKey() {
+        Map result = new LinkedHashMap();
+        result.put("alg", signer.algorithm());
+        result.put("value", verifierKey);
+        return result;
+    }
 
-	public void setKeyPair(KeyPair keyPair) {
-			PrivateKey privateKey = keyPair.getPrivate();
-			Assert.state(privateKey instanceof RSAPrivateKey, "KeyPair must be an RSA ");
-			signer = new RsaSigner((RSAPrivateKey) privateKey);
-			RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
-			verifier = new RsaVerifier(publicKey);
-			verifierKey = "-----BEGIN PUBLIC KEY-----\n" + new String(Base64.encode(publicKey.getEncoded())) + "\n-----END PUBLIC KEY-----";
-		}
-	/**
-	 * Sets the JWT signing key. It can be either a simple MAC key or an RSA key. RSA keys should be in OpenSSH format,
-	 * as produced by ssh-keygen.
-	 * 
-	 * @param key the key to be used for signing JWTs.
-	 */
-	public void setSigningKey(String key) {
-		Assert.hasText(key);
-		key = key.trim();
+    public void setKeyPair(KeyPair keyPair) {
+        PrivateKey privateKey = keyPair.getPrivate();
+        Assert.state(privateKey instanceof RSAPrivateKey, "KeyPair must be an RSA ");
+        signer = new RsaSigner((RSAPrivateKey) privateKey);
+        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
+        verifier = new RsaVerifier(publicKey);
+        verifierKey = "-----BEGIN PUBLIC KEY-----\n" + new String(Base64.encode(publicKey.getEncoded()))
+                + "\n-----END PUBLIC KEY-----";
+    }
 
-		this.signingKey = key;
+    /**
+     * Sets the JWT signing key. It can be either a simple MAC key or an RSA key.
+     * RSA keys should be in OpenSSH format, as produced by ssh-keygen.
+     * 
+     * @param key the key to be used for signing JWTs.
+     */
+    public void setSigningKey(String key) {
+        Assert.hasText(key);
+        key = key.trim();
 
-		if (isPublic(key)) {
-			signer = new RsaSigner(key);
-			logger.info("Configured with RSA signing key");
-		}
-		else {
-			// Assume it's a MAC key
-			this.verifierKey = key;
-			signer = new MacSigner(key);
-		}
-	}
+        this.signingKey = key;
 
-	/**
-	 * @return true if the key has a public verifier
-	 */
-	private boolean isPublic(String key) {
-		return key.startsWith("-----BEGIN");
-	}
-	
-	/**
-	 * @return true if the signing key is a public key
-	 */
-	public boolean isPublic() {
-		return signer instanceof RsaSigner;
-	}
+        if (isPublic(key)) {
+            signer = new RsaSigner(key);
+            logger.info("Configured with RSA signing key");
+        } else {
+            // Assume it's a MAC key
+            this.verifierKey = key;
+            signer = new MacSigner(key);
+        }
+    }
 
-	/**
-	 * The key used for verifying signatures produced by this class. This is not used but is returned from the endpoint
-	 * to allow resource servers to obtain the key.
-	 * 
-	 * For an HMAC key it will be the same value as the signing key and does not need to be set. For and RSA key, it
-	 * should be set to the String representation of the public key, in a standard format (e.g. OpenSSH keys)
-	 * 
-	 * @param key the signature verification key (typically an RSA public key)
-	 */
-	public void setVerifierKey(String key) {
-		this.verifierKey = key;
-		try {
-			new RsaSigner(verifierKey);
-			throw new IllegalArgumentException("Private key cannot be set as verifierKey property");
-		}
-		catch (Exception expected) {
-			// Expected
-		}
-	}
+    /**
+     * @return true if the key has a public verifier
+     */
+    private boolean isPublic(String key) {
+        return key.startsWith("-----BEGIN");
+    }
 
-	public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
-		DefaultOAuth2AccessToken result = new DefaultOAuth2AccessToken(accessToken);
-		Map info = new LinkedHashMap(accessToken.getAdditionalInformation());
-		String tokenId = result.getValue();
-		if (!info.containsKey(TOKEN_ID)) {
-			info.put(TOKEN_ID, tokenId);
-		}
-		result.setAdditionalInformation(info);
-		result.setValue(encode(result, authentication));
-		OAuth2RefreshToken refreshToken = result.getRefreshToken();
-		if (refreshToken != null) {
-			DefaultOAuth2AccessToken encodedRefreshToken = new DefaultOAuth2AccessToken(accessToken);
-			encodedRefreshToken.setValue(refreshToken.getValue());
-			Map refreshTokenInfo = new LinkedHashMap(accessToken.getAdditionalInformation());
-			refreshTokenInfo.put(TOKEN_ID, encodedRefreshToken.getValue());
-			encodedRefreshToken.setAdditionalInformation(refreshTokenInfo);
-			DefaultOAuth2RefreshToken token = new DefaultOAuth2RefreshToken(encode(encodedRefreshToken, authentication));
-			if (refreshToken instanceof ExpiringOAuth2RefreshToken) {
-				Date expiration = ((ExpiringOAuth2RefreshToken) refreshToken).getExpiration();
-				encodedRefreshToken.setExpiration(expiration);
-				token = new DefaultExpiringOAuth2RefreshToken(encode(encodedRefreshToken, authentication), expiration);
-			}
-			result.setRefreshToken(token);
-		}
-		return result;
-	}
+    /**
+     * @return true if the signing key is a public key
+     */
+    public boolean isPublic() {
+        return signer instanceof RsaSigner;
+    }
 
-	protected String encode(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
-		String content;
-		try {
-			content = objectMapper.formatMap(tokenConverter.convertAccessToken(accessToken, authentication));
-		}
-		catch (Exception e) {
-			throw new IllegalStateException("Cannot convert access token to JSON", e);
-		}
-		String token = JwtHelper.encode(content, signer).getEncoded();
-		return token;
-	}
+    /**
+     * The key used for verifying signatures produced by this class. This is not
+     * used but is returned from the endpoint to allow resource servers to obtain
+     * the key.
+     * 
+     * For an HMAC key it will be the same value as the signing key and does not
+     * need to be set. For and RSA key, it should be set to the String
+     * representation of the public key, in a standard format (e.g. OpenSSH keys)
+     * 
+     * @param key the signature verification key (typically an RSA public key)
+     */
+    public void setVerifierKey(String key) {
+        this.verifierKey = key;
+        try {
+            new RsaSigner(verifierKey);
+            throw new IllegalArgumentException("Private key cannot be set as verifierKey property");
+        } catch (Exception expected) {
+            // Expected
+        }
+    }
 
-	protected Map decode(String token) {
-		try {
-			Jwt jwt = JwtHelper.decodeAndVerify(token, verifier);
-			String content = jwt.getClaims();
-			Map map = objectMapper.parseMap(content);
-			if (map.containsKey(EXP) && map.get(EXP) instanceof Integer) {
-				Integer intValue = (Integer) map.get(EXP);
-				map.put(EXP, new Long(intValue));
-			}
-			return map;
-		}
-		catch (Exception e) {
-			throw new InvalidTokenException("Cannot convert access token to JSON", e);
-		}
-	}
+    public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
+        DefaultOAuth2AccessToken result = new DefaultOAuth2AccessToken(accessToken);
+        Map info = new LinkedHashMap(accessToken.getAdditionalInformation());
+        String tokenId = result.getValue();
+        if (!info.containsKey(TOKEN_ID)) {
+            info.put(TOKEN_ID, tokenId);
+        }
+        result.setAdditionalInformation(info);
+        result.setValue(encode(result, authentication));
+        OAuth2RefreshToken refreshToken = result.getRefreshToken();
+        if (refreshToken != null) {
+            DefaultOAuth2AccessToken encodedRefreshToken = new DefaultOAuth2AccessToken(accessToken);
+            encodedRefreshToken.setValue(refreshToken.getValue());
+            Map refreshTokenInfo = new LinkedHashMap(
+                    accessToken.getAdditionalInformation());
+            refreshTokenInfo.put(TOKEN_ID, encodedRefreshToken.getValue());
+            encodedRefreshToken.setAdditionalInformation(refreshTokenInfo);
+            DefaultOAuth2RefreshToken token = new DefaultOAuth2RefreshToken(
+                    encode(encodedRefreshToken, authentication));
+            if (refreshToken instanceof ExpiringOAuth2RefreshToken) {
+                Date expiration = ((ExpiringOAuth2RefreshToken) refreshToken).getExpiration();
+                encodedRefreshToken.setExpiration(expiration);
+                token = new DefaultExpiringOAuth2RefreshToken(encode(encodedRefreshToken, authentication), expiration);
+            }
+            result.setRefreshToken(token);
+        }
+        return result;
+    }
 
-	public void afterPropertiesSet() throws Exception {
-		// Check the signing and verification keys match
-		if (signer instanceof RsaSigner) {
-			RsaVerifier verifier;
-			try {
-				verifier = new RsaVerifier(verifierKey);
-			}
-			catch (Exception e) {
-				logger.warn("Unable to create an RSA verifier from verifierKey");
-				return;
-			}
+    protected String encode(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
+        String content;
+        try {
+            content = objectMapper.formatMap(tokenConverter.convertAccessToken(accessToken, authentication));
+        } catch (Exception e) {
+            throw new IllegalStateException("Cannot convert access token to JSON", e);
+        }
+        String token = JwtHelper.encode(content, signer).getEncoded();
+        return token;
+    }
 
-			byte[] test = "test".getBytes();
-			try {
-				verifier.verify(test, signer.sign(test));
-				logger.info("Signing and verification RSA keys match");
-			}
-			catch (InvalidSignatureException e) {
-				logger.error("Signing and verification RSA keys do not match");
-			}
-		}
-		else {
-			// Avoid a race condition where setters are called in the wrong order. Use of == is intentional.
-			Assert.state(this.signingKey == this.verifierKey,
-					"For MAC signing you do not need to specify the verifier key separately, and if you do it must match the signing key");
-		}
-		SignatureVerifier verifier = new MacSigner(verifierKey);
-		try {
-			verifier = new RsaVerifier(verifierKey);
-		}
-		catch (Exception e) {
-			logger.warn("Unable to create an RSA verifier from verifierKey");
-		}
-		this.verifier = verifier;
-	}
+    protected Map decode(String token) {
+        try {
+            Jwt jwt = JwtHelper.decodeAndVerify(token, verifier);
+            String content = jwt.getClaims();
+            Map map = objectMapper.parseMap(content);
+            if (map.containsKey(EXP) && map.get(EXP) instanceof Integer) {
+                Integer intValue = (Integer) map.get(EXP);
+                map.put(EXP, new Long(intValue));
+            }
+            return map;
+        } catch (Exception e) {
+            throw new InvalidTokenException("Cannot convert access token to JSON", e);
+        }
+    }
+
+    public void afterPropertiesSet() throws Exception {
+        // Check the signing and verification keys match
+        if (signer instanceof RsaSigner) {
+            RsaVerifier verifier;
+            try {
+                verifier = new RsaVerifier(verifierKey);
+            } catch (Exception e) {
+                logger.warn("Unable to create an RSA verifier from verifierKey");
+                return;
+            }
+
+            byte[] test = "test".getBytes();
+            try {
+                verifier.verify(test, signer.sign(test));
+                logger.info("Signing and verification RSA keys match");
+            } catch (InvalidSignatureException e) {
+                logger.error("Signing and verification RSA keys do not match");
+            }
+        } else {
+            // Avoid a race condition where setters are called in the wrong order. Use of ==
+            // is intentional.
+            Assert.state(this.signingKey == this.verifierKey,
+                    "For MAC signing you do not need to specify the verifier key separately, and if you do it must match the signing key");
+        }
+        SignatureVerifier verifier = new MacSigner(verifierKey);
+        try {
+            verifier = new RsaVerifier(verifierKey);
+        } catch (Exception e) {
+            logger.warn("Unable to create an RSA verifier from verifierKey");
+        }
+        this.verifier = verifier;
+    }
 }
diff --git a/maxkey-web-maxkey/src/main/java/org/maxkey/MaxKeyConfig.java b/maxkey-web-maxkey/src/main/java/org/maxkey/MaxKeyConfig.java
index 8096349e6..0637d6e0d 100644
--- a/maxkey-web-maxkey/src/main/java/org/maxkey/MaxKeyConfig.java
+++ b/maxkey-web-maxkey/src/main/java/org/maxkey/MaxKeyConfig.java
@@ -34,7 +34,14 @@ import org.springframework.context.annotation.PropertySource;
         "org.maxkey.domain.userinfo",
         "org.maxkey.api.v1.contorller",
         "org.maxkey.web.endpoint",
-        "org.maxkey.web.contorller"
+        "org.maxkey.web.contorller",
+        //single sign on protocol
+        "org.maxkey.authz.endpoint",
+        "org.maxkey.authz.desktop.endpoint",
+        "org.maxkey.authz.exapi.endpoint",
+        "org.maxkey.authz.formbased.endpoint",
+        "org.maxkey.authz.ltpa.endpoint",
+        "org.maxkey.authz.token.endpoint",
 })
 public class MaxKeyConfig  implements InitializingBean {
     private static final  Logger _logger = LoggerFactory.getLogger(MaxKeyConfig.class);
diff --git a/maxkey-web-maxkey/src/main/java/org/maxkey/autoconfigure/CasAutoConfiguration.java b/maxkey-web-maxkey/src/main/java/org/maxkey/autoconfigure/CasAutoConfiguration.java
new file mode 100644
index 000000000..88d6332ae
--- /dev/null
+++ b/maxkey-web-maxkey/src/main/java/org/maxkey/autoconfigure/CasAutoConfiguration.java
@@ -0,0 +1,58 @@
+package org.maxkey.autoconfigure;
+
+import org.maxkey.authz.cas.endpoint.ticket.service.InMemoryTicketServices;
+import org.maxkey.authz.cas.endpoint.ticket.service.JdbcTicketServices;
+import org.maxkey.authz.cas.endpoint.ticket.service.RedisTicketServices;
+import org.maxkey.authz.cas.endpoint.ticket.service.TicketServices;
+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.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.jdbc.core.JdbcTemplate;
+
+@Configuration
+@ComponentScan(basePackages = {
+        "org.maxkey.authz.cas.endpoint"
+})
+@PropertySource("classpath:/application.properties")
+public class CasAutoConfiguration implements InitializingBean {
+    private static final  Logger _logger = LoggerFactory.getLogger(CasAutoConfiguration.class);
+    
+    /**
+     * TicketServices. 
+     * @param persistence int
+     * @param validity int
+     * @return casTicketServices
+     */
+    @Bean(name = "casTicketServices")
+    public TicketServices casTicketServices(
+            @Value("${config.server.persistence}") int persistence,
+            @Value("${config.login.remeberme.validity}") int validity,
+            JdbcTemplate jdbcTemplate,
+            RedisConnectionFactory jedisConnectionFactory) {
+        TicketServices casTicketServices = null;
+        if (persistence == 0) {
+            casTicketServices = new InMemoryTicketServices();
+            _logger.debug("InMemoryTicketServices");
+        } else if (persistence == 1) {
+            casTicketServices = new JdbcTicketServices(jdbcTemplate);
+            _logger.debug("JdbcTicketServices");
+        } else if (persistence == 2) {
+            casTicketServices = new RedisTicketServices(jedisConnectionFactory);
+            _logger.debug("RedisTicketServices");
+        }
+        return casTicketServices;
+    }
+   
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        // TODO Auto-generated method stub
+        
+    }
+}
diff --git a/maxkey-web-maxkey/src/main/java/org/maxkey/autoconfigure/Oauth20AutoConfiguration.java b/maxkey-web-maxkey/src/main/java/org/maxkey/autoconfigure/Oauth20AutoConfiguration.java
new file mode 100644
index 000000000..4c9536c5c
--- /dev/null
+++ b/maxkey-web-maxkey/src/main/java/org/maxkey/autoconfigure/Oauth20AutoConfiguration.java
@@ -0,0 +1,334 @@
+package org.maxkey.autoconfigure;
+
+import java.net.URI;
+import java.security.NoSuchAlgorithmException;
+import java.security.spec.InvalidKeySpecException;
+
+import javax.sql.DataSource;
+
+import org.maxkey.authn.support.jwt.JwtLoginService;
+import org.maxkey.authz.oauth2.provider.ClientDetailsService;
+import org.maxkey.authz.oauth2.provider.approval.TokenApprovalStore;
+import org.maxkey.authz.oauth2.provider.approval.controller.OAuth20UserApprovalHandler;
+import org.maxkey.authz.oauth2.provider.client.ClientDetailsUserDetailsService;
+import org.maxkey.authz.oauth2.provider.client.JdbcClientDetailsService;
+import org.maxkey.authz.oauth2.provider.code.AuthorizationCodeServices;
+import org.maxkey.authz.oauth2.provider.code.InMemoryAuthorizationCodeServices;
+import org.maxkey.authz.oauth2.provider.code.JdbcAuthorizationCodeServices;
+import org.maxkey.authz.oauth2.provider.code.RedisAuthorizationCodeServices;
+import org.maxkey.authz.oauth2.provider.request.DefaultOAuth2RequestFactory;
+import org.maxkey.authz.oauth2.provider.token.TokenStore;
+import org.maxkey.authz.oauth2.provider.token.DefaultTokenServices;
+import org.maxkey.authz.oauth2.provider.token.store.InMemoryTokenStore;
+import org.maxkey.authz.oauth2.provider.token.store.JdbcTokenStore;
+import org.maxkey.authz.oauth2.provider.token.store.JwtAccessTokenConverter;
+import org.maxkey.authz.oauth2.provider.token.store.RedisTokenStore;
+import org.maxkey.authz.oidc.idtoken.OIDCIdTokenEnhancer;
+import org.maxkey.config.oidc.OIDCProviderMetadataDetails;
+import org.maxkey.crypto.jose.keystore.JWKSetKeyStore;
+import org.maxkey.crypto.jwt.encryption.service.impl.DefaultJwtEncryptionAndDecryptionService;
+import org.maxkey.crypto.jwt.signer.service.impl.DefaultJwtSigningAndValidationService;
+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.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.core.io.ClassPathResource;
+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.NoOpPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.JWEAlgorithm;
+
+@Configuration
+@ComponentScan(basePackages = {
+        "org.maxkey.authz.oauth2.provider.endpoint",
+        "org.maxkey.authz.oauth2.provider.userinfo.endpoint",
+        "org.maxkey.authz.oauth2.provider.approval.controller"
+})
+@PropertySource("classpath:/application.properties")
+@PropertySource("classpath:/config/applicationConfig.properties")
+public class Oauth20AutoConfiguration implements InitializingBean {
+    private static final  Logger _logger = LoggerFactory.getLogger(Oauth20AutoConfiguration.class);
+    
+    /**
+     * OIDCProviderMetadataDetails. 
+     * Self-issued Provider Metadata
+     * http://openid.net/specs/openid-connect-core-1_0.html#SelfIssued 
+     */
+    @Bean(name = "oidcProviderMetadata")
+    public OIDCProviderMetadataDetails OIDCProviderMetadataDetails(
+            @Value("${config.oidc.metadata.issuer}")
+            String issuer,
+            @Value("${config.oidc.metadata.authorizationEndpoint}")
+            URI authorizationEndpoint,
+            @Value("${config.oidc.metadata.tokenEndpoint}")
+            URI tokenEndpoint,
+            @Value("${config.oidc.metadata.userinfoEndpoint}")
+            URI userinfoEndpoint) {
+        _logger.debug("RedisConnectionFactory init .");
+        OIDCProviderMetadataDetails oidcProviderMetadata = new OIDCProviderMetadataDetails();
+        oidcProviderMetadata.setIssuer(issuer);
+        oidcProviderMetadata.setAuthorizationEndpoint(authorizationEndpoint);
+        oidcProviderMetadata.setTokenEndpoint(tokenEndpoint);
+        oidcProviderMetadata.setUserinfoEndpoint(userinfoEndpoint);
+        return oidcProviderMetadata;
+    }
+
+    /**
+     * jwtSetKeyStore.
+     * @return
+     */
+    @Bean(name = "jwkSetKeyStore")
+    public JWKSetKeyStore jwtSetKeyStore() {
+        JWKSetKeyStore jwkSetKeyStore = new JWKSetKeyStore();
+        ClassPathResource classPathResource = new ClassPathResource("/config/keystore.jwks");
+        jwkSetKeyStore.setLocation(classPathResource);
+        return jwkSetKeyStore;
+    }
+    
+    /**
+     * jwtSetKeyStore.
+     * @return
+     * @throws JOSEException
+     * @throws InvalidKeySpecException 
+     * @throws NoSuchAlgorithmException 
+     */
+    @Bean(name = "jwtSignerValidationService")
+    public DefaultJwtSigningAndValidationService jwtSignerValidationService(
+            JWKSetKeyStore jwtSetKeyStore) 
+                    throws NoSuchAlgorithmException, InvalidKeySpecException, JOSEException {
+        DefaultJwtSigningAndValidationService jwtSignerValidationService = 
+                new DefaultJwtSigningAndValidationService(jwtSetKeyStore);
+        jwtSignerValidationService.setDefaultSignerKeyId("maxkey_rsa");
+        jwtSignerValidationService.setDefaultSigningAlgorithmName("RS256");
+        return jwtSignerValidationService;
+    }
+    
+    /**
+     * jwtSetKeyStore.
+     * @return
+     * @throws JOSEException 
+     * @throws InvalidKeySpecException 
+     * @throws NoSuchAlgorithmException 
+     */
+    @Bean(name = "jwtEncryptionService")
+    public DefaultJwtEncryptionAndDecryptionService jwtEncryptionService(
+            JWKSetKeyStore jwtSetKeyStore) 
+                    throws NoSuchAlgorithmException, InvalidKeySpecException, JOSEException {
+        DefaultJwtEncryptionAndDecryptionService jwtEncryptionService = 
+                new DefaultJwtEncryptionAndDecryptionService(jwtSetKeyStore);
+        jwtEncryptionService.setDefaultAlgorithm(JWEAlgorithm.RSA1_5);//RSA1_5
+        jwtEncryptionService.setDefaultDecryptionKeyId("maxkey_rsa");
+        jwtEncryptionService.setDefaultEncryptionKeyId("maxkey_rsa");
+        return jwtEncryptionService;
+    }
+    
+    /**
+     * JwtLoginService.
+     * @return
+     */
+    @Bean(name = "jwtLoginService")
+    public JwtLoginService jwtLoginService(
+            DefaultJwtSigningAndValidationService jwtSignerValidationService,
+            OIDCProviderMetadataDetails oidcProviderMetadata) {
+        JwtLoginService jwkSetKeyStore = new JwtLoginService();
+        jwkSetKeyStore.setJwtSignerValidationService(jwtSignerValidationService);
+        jwkSetKeyStore.setJwtProviderMetadata(oidcProviderMetadata);
+        return jwkSetKeyStore;
+    }
+    
+    
+    /**
+     * tokenEnhancer.
+     * @return
+     */
+    @Bean(name = "tokenEnhancer")
+    public OIDCIdTokenEnhancer tokenEnhancer(
+            DefaultJwtSigningAndValidationService jwtSignerValidationService,
+            DefaultJwtEncryptionAndDecryptionService jwtEncryptionService,
+            OIDCProviderMetadataDetails oidcProviderMetadata,
+            ClientDetailsService oauth20JdbcClientDetailsService) {
+        OIDCIdTokenEnhancer tokenEnhancer = new OIDCIdTokenEnhancer();
+        tokenEnhancer.setJwtSignerService(jwtSignerValidationService);
+        tokenEnhancer.setJwtEnDecryptionService(jwtEncryptionService);
+        tokenEnhancer.setClientDetailsService(oauth20JdbcClientDetailsService);
+        tokenEnhancer.setProviderMetadata(oidcProviderMetadata);
+        return tokenEnhancer;
+    }
+    //以上部分为了支持OpenID Connect 1.0
+    
+    
+    /**
+     * AuthorizationCodeServices. 
+     * @param persistence int
+     * @return oauth20AuthorizationCodeServices
+     */
+    @Bean(name = "oauth20AuthorizationCodeServices")
+    public AuthorizationCodeServices oauth20AuthorizationCodeServices(
+            @Value("${config.server.persistence}") int persistence,
+            JdbcTemplate jdbcTemplate,
+            RedisConnectionFactory jedisConnectionFactory) {
+        AuthorizationCodeServices authorizationCodeServices = null;
+        if (persistence == 0) {
+            authorizationCodeServices = new InMemoryAuthorizationCodeServices();
+            _logger.debug("InMemoryAuthorizationCodeServices");
+        } else if (persistence == 1) {
+            authorizationCodeServices = new JdbcAuthorizationCodeServices(jdbcTemplate);
+            _logger.debug("JdbcAuthorizationCodeServices");
+        } else if (persistence == 2) {
+            authorizationCodeServices = new RedisAuthorizationCodeServices(jedisConnectionFactory);
+            _logger.debug("RedisAuthorizationCodeServices");
+        }
+        return authorizationCodeServices;
+    }
+    
+    /**
+     * TokenStore. 
+     * @param persistence int
+     * @return oauth20TokenStore
+     */
+    @Bean(name = "oauth20TokenStore")
+    public TokenStore oauth20TokenStore(
+            @Value("${config.server.persistence}") int persistence,
+            JdbcTemplate jdbcTemplate,
+            RedisConnectionFactory jedisConnectionFactory) {
+        TokenStore tokenStore = null;
+        if (persistence == 0) {
+            tokenStore = new InMemoryTokenStore();
+            _logger.debug("InMemoryTokenStore");
+        } else if (persistence == 1) {
+            tokenStore = new JdbcTokenStore(jdbcTemplate);
+            _logger.debug("JdbcTokenStore");
+        } else if (persistence == 2) {
+            tokenStore = new RedisTokenStore(jedisConnectionFactory);
+            _logger.debug("RedisTokenStore");
+        }
+        return tokenStore;
+    }
+    
+    /**
+     * jwtAccessTokenConverter. 
+     * @return converter
+     */
+    @Bean(name = "converter")
+    public JwtAccessTokenConverter jwtAccessTokenConverter() {
+        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
+        return jwtAccessTokenConverter;
+    }
+    
+    /**
+     * clientDetailsService. 
+     * @return oauth20JdbcClientDetailsService
+     */
+    @Bean(name = "oauth20JdbcClientDetailsService")
+    public JdbcClientDetailsService clientDetailsService(DataSource dataSource,PasswordEncoder passwordReciprocal) {
+        JdbcClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
+        clientDetailsService.setPasswordEncoder(passwordReciprocal);
+        return clientDetailsService;
+    }
+    
+    /**
+     * clientDetailsUserDetailsService. 
+     * @return oauth20ClientDetailsUserService
+     */
+    @Bean(name = "oauth20ClientDetailsUserService")
+    public ClientDetailsUserDetailsService clientDetailsUserDetailsService(
+            JdbcClientDetailsService oauth20JdbcClientDetailsService,PasswordEncoder passwordReciprocal) {
+        ClientDetailsUserDetailsService cientDetailsUserDetailsService = 
+                new ClientDetailsUserDetailsService(oauth20JdbcClientDetailsService);
+        cientDetailsUserDetailsService.setPasswordEncoder(passwordReciprocal);
+        return cientDetailsUserDetailsService;
+    }
+    
+    
+    /**
+     * clientDetailsUserDetailsService. 
+     * @return oauth20TokenServices
+     */
+    @Bean(name = "oauth20TokenServices")
+    public DefaultTokenServices DefaultTokenServices(
+            JdbcClientDetailsService oauth20JdbcClientDetailsService,
+            TokenStore oauth20TokenStore,
+            OIDCIdTokenEnhancer tokenEnhancer) {
+        DefaultTokenServices tokenServices = new DefaultTokenServices();
+        tokenServices.setClientDetailsService(oauth20JdbcClientDetailsService);
+        tokenServices.setTokenEnhancer(tokenEnhancer);
+        tokenServices.setTokenStore(oauth20TokenStore);
+        tokenServices.setSupportRefreshToken(true);
+        return tokenServices;
+    }
+    
+    
+    /**
+     * TokenApprovalStore. 
+     * @return oauth20ApprovalStore
+     */
+    @Bean(name = "oauth20ApprovalStore")
+    public TokenApprovalStore tokenApprovalStore(
+            TokenStore oauth20TokenStore) {
+        TokenApprovalStore tokenApprovalStore = new TokenApprovalStore();
+        tokenApprovalStore.setTokenStore(oauth20TokenStore);
+        return tokenApprovalStore;
+    }
+    
+    
+    /**
+     * OAuth2RequestFactory. 
+     * @return oAuth2RequestFactory
+     */
+    @Bean(name = "oAuth2RequestFactory")
+    public DefaultOAuth2RequestFactory oauth2RequestFactory(
+            JdbcClientDetailsService oauth20JdbcClientDetailsService) {
+        DefaultOAuth2RequestFactory oauth2RequestFactory = 
+                new DefaultOAuth2RequestFactory(oauth20JdbcClientDetailsService);
+        return oauth2RequestFactory;
+    }
+    
+    /**
+     * OAuth20UserApprovalHandler. 
+     * @return oauth20UserApprovalHandler
+     */
+    @Bean(name = "oauth20UserApprovalHandler")
+    public OAuth20UserApprovalHandler oauth20UserApprovalHandler(
+            JdbcClientDetailsService oauth20JdbcClientDetailsService,
+            DefaultOAuth2RequestFactory oAuth2RequestFactory,
+            TokenApprovalStore oauth20ApprovalStore
+            ) {
+        OAuth20UserApprovalHandler userApprovalHandler = new OAuth20UserApprovalHandler();
+        userApprovalHandler.setApprovalStore(oauth20ApprovalStore);
+        userApprovalHandler.setRequestFactory(oAuth2RequestFactory);
+        userApprovalHandler.setClientDetailsService(oauth20JdbcClientDetailsService);
+        return userApprovalHandler;
+    }
+    
+    /**
+     * ProviderManager. 
+     * @return oauth20ClientAuthenticationManager
+     */
+    @Bean(name = "oauth20ClientAuthenticationManager")
+    public ProviderManager oauth20ClientAuthenticationManager(
+            ClientDetailsUserDetailsService oauth20ClientDetailsUserService
+            ) {
+        DaoAuthenticationProvider daoAuthenticationProvider= new DaoAuthenticationProvider();
+        PasswordEncoder passwordEncoder = NoOpPasswordEncoder.getInstance();
+        daoAuthenticationProvider.setPasswordEncoder(passwordEncoder);
+        daoAuthenticationProvider.setUserDetailsService(oauth20ClientDetailsUserService);
+        ProviderManager clientAuthenticationManager = new ProviderManager(daoAuthenticationProvider);
+        return clientAuthenticationManager;
+    }
+    
+    
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        // TODO Auto-generated method stub
+        
+    }
+}
diff --git a/maxkey-web-maxkey/src/main/java/org/maxkey/autoconfigure/Saml20AutoConfiguration.java b/maxkey-web-maxkey/src/main/java/org/maxkey/autoconfigure/Saml20AutoConfiguration.java
new file mode 100644
index 000000000..24e34894d
--- /dev/null
+++ b/maxkey-web-maxkey/src/main/java/org/maxkey/autoconfigure/Saml20AutoConfiguration.java
@@ -0,0 +1,322 @@
+package org.maxkey.autoconfigure;
+
+import java.io.IOException;
+import java.util.Properties;
+
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.exception.VelocityException;
+import org.maxkey.authz.saml.common.EndpointGenerator;
+import org.maxkey.authz.saml.service.IDService;
+import org.maxkey.authz.saml.service.TimeService;
+import org.maxkey.authz.saml20.binding.decoder.OpenHTTPPostDecoder;
+import org.maxkey.authz.saml20.binding.decoder.OpenHTTPPostSimpleSignDecoder;
+import org.maxkey.authz.saml20.binding.decoder.OpenHTTPRedirectDecoder;
+import org.maxkey.authz.saml20.binding.impl.ExtractPostBindingAdapter;
+import org.maxkey.authz.saml20.binding.impl.ExtractRedirectBindingAdapter;
+import org.maxkey.authz.saml20.binding.impl.PostBindingAdapter;
+import org.maxkey.authz.saml20.binding.impl.PostSimpleSignBindingAdapter;
+import org.maxkey.authz.saml20.provider.xml.AuthnResponseGenerator;
+import org.maxkey.authz.saml20.xml.SAML2ValidatorSuite;
+import org.maxkey.crypto.keystore.KeyStoreLoader;
+import org.maxkey.domain.Saml20Metadata;
+import org.opensaml.common.binding.security.IssueInstantRule;
+import org.opensaml.common.binding.security.MessageReplayRule;
+import org.opensaml.util.storage.MapBasedStorageService;
+import org.opensaml.util.storage.ReplayCache;
+import org.opensaml.xml.ConfigurationException;
+import org.opensaml.xml.parse.BasicParserPool;
+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.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.ui.velocity.VelocityEngineFactoryBean;
+
+@Configuration
+@ComponentScan(basePackages = {
+        "org.maxkey.authz.saml20.provider.endpoint",
+        "org.maxkey.authz.saml20.metadata.endpoint",
+})
+@PropertySource("classpath:/application.properties")
+@PropertySource("classpath:/config/applicationConfig.properties")
+public class Saml20AutoConfiguration implements InitializingBean {
+    private static final  Logger _logger = LoggerFactory.getLogger(Saml20AutoConfiguration.class);
+    
+    /**
+     * samlBootstrapInitializer.
+     * @return samlBootstrapInitializer
+     * @throws ConfigurationException 
+     */
+    @Bean(name = "samlBootstrapInitializer")
+    public String samlBootstrapInitializer() throws ConfigurationException {
+        org.opensaml.DefaultBootstrap.bootstrap();
+        return "";
+    }
+    
+    /**
+     * TimeService.
+     * @return timeService
+     */
+    @Bean(name = "timeService")
+    public TimeService TimeService() {
+        TimeService timeService = new TimeService();
+        return timeService;
+    }
+    
+    /**
+     * IDService.
+     * @return idService
+     */
+    @Bean(name = "idService")
+    public IDService idService() {
+        IDService idService = new IDService();
+        return idService;
+    }
+    
+    /**
+     * EndpointGenerator.
+     * @return endpointGenerator
+     */
+    @Bean(name = "endpointGenerator")
+    public EndpointGenerator endpointGenerator() {
+        EndpointGenerator generator = new EndpointGenerator();
+        return generator;
+    }
+    
+    /**
+     * AuthnResponseGenerator.
+     * @return authnResponseGenerator
+     */
+    @Bean(name = "authnResponseGenerator")
+    public AuthnResponseGenerator authnResponseGenerator(TimeService timeService,IDService idService,
+            @Value("${config.saml.v20.idp.issuer}") String issuerEntityName) {
+        AuthnResponseGenerator generator = new AuthnResponseGenerator(issuerEntityName,timeService,idService);
+        return generator;
+    }
+    
+    /**
+     * IssuerEntityName.
+     * @return issuerEntityName
+     */
+    @Bean(name = "issuerEntityName")
+    public String issuerEntityName(
+            @Value("${config.saml.v20.idp.issuer}") String issuerEntityName) {
+        return issuerEntityName;
+    }
+    
+    /**
+     * Saml20Metadata.
+     * @return saml20Metadata
+     */
+    @Bean(name = "saml20Metadata")
+    public Saml20Metadata saml20Metadata(
+            @Value("${config.saml.v20.metadata.orgName}") String orgName,
+            @Value("${config.saml.v20.metadata.orgDisplayName}") String orgDisplayName,
+            @Value("${config.saml.v20.metadata.orgURL}") String orgURL,
+            @Value("${config.saml.v20.metadata.company}") String company,
+            @Value("${config.saml.v20.metadata.contactType}") String contactType,
+            @Value("${config.saml.v20.metadata.givenName}") String givenName,
+            @Value("${config.saml.v20.metadata.surName}") String surName,
+            @Value("${config.saml.v20.metadata.emailAddress}") String emailAddress,
+            @Value("${config.saml.v20.metadata.telephoneNumber}") String telephoneNumber) {
+        Saml20Metadata metadata = new Saml20Metadata();
+        metadata.setOrgName(orgName);
+        metadata.setOrgDisplayName(orgDisplayName);
+        metadata.setOrgURL(orgURL);
+        metadata.setCompany(company);
+        metadata.setContactType(contactType);
+        metadata.setGivenName(givenName);
+        metadata.setSurName(surName);
+        metadata.setEmailAddress(emailAddress);
+        metadata.setTelephoneNumber(telephoneNumber);
+        return metadata;
+    }
+
+    /**
+     * SAML2ValidatorSuite.
+     * @return samlValidaotrSuite
+     */
+    @Bean(name = "samlValidaotrSuite")
+    public SAML2ValidatorSuite validatorSuite() {
+        SAML2ValidatorSuite validatorSuite = new SAML2ValidatorSuite();
+        return validatorSuite;
+    }
+    
+    /**
+     * MapBasedStorageService.
+     * @return mapBasedStorageService
+     */
+    @Bean(name = "mapBasedStorageService")
+    public MapBasedStorageService mapBasedStorageService() {
+        MapBasedStorageService mapBasedStorageService = new MapBasedStorageService();
+        return mapBasedStorageService;
+    }
+    
+    /**
+     * VelocityEngineFactoryBean.
+     * @return velocityEngine
+     * @throws IOException 
+     * @throws VelocityException 
+     */
+    @Bean(name = "velocityEngine")
+    public VelocityEngine velocityEngine() throws VelocityException, IOException {
+        VelocityEngineFactoryBean factory = new VelocityEngineFactoryBean();
+        factory.setPreferFileSystemAccess(false);
+        Properties velocityProperties = new Properties();
+        velocityProperties.put("resource.loader", "classpath");
+        velocityProperties.put("classpath.resource.loader.class", 
+                "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
+        factory.setVelocityProperties(velocityProperties);
+        return factory.createVelocityEngine();
+    }
+    
+    /**
+     * ReplayCache.
+     * @return replayCache
+     */
+    @Bean(name = "replayCache")
+    public ReplayCache replayCache(MapBasedStorageService mapBasedStorageService,
+            @Value("${config.saml.v20.replay.cache.life.in.millis}") long duration) {
+        ReplayCache replayCache = new ReplayCache(mapBasedStorageService,duration);
+        return replayCache;
+    }
+    
+    /**
+     * MessageReplayRule.
+     * @return messageReplayRule
+     */
+    @Bean(name = "messageReplayRule")
+    public MessageReplayRule messageReplayRule(ReplayCache replayCache) {
+        MessageReplayRule messageReplayRule = new MessageReplayRule(replayCache);
+        return messageReplayRule;
+    }
+    
+    /**
+     * BasicParserPool.
+     * @return samlParserPool
+     */
+    @Bean(name = "samlParserPool")
+    public BasicParserPool samlParserPool(
+            @Value("${config.saml.v20.max.parser.pool.size}") int maxPoolSize) {
+        BasicParserPool samlParserPool = new BasicParserPool();
+        samlParserPool.setMaxPoolSize(maxPoolSize);
+        return samlParserPool;
+    }
+    
+    /**
+     * IssueInstantRule.
+     * @return issueInstantRule
+     */
+    @Bean(name = "issueInstantRule")
+    public IssueInstantRule issueInstantRule(
+            @Value("${config.saml.v20.issue.instant.check.clock.skew.in.seconds}") int newClockSkew,
+            @Value("${config.saml.v20.issue.instant.check.validity.time.in.seconds}") int newExpires) {
+        IssueInstantRule decoder = new IssueInstantRule(newClockSkew,newExpires);
+        decoder.setRequiredRule(true);
+        return decoder;
+    }
+    
+    /**
+     * OpenHTTPPostSimpleSignDecoder.
+     * @return openHTTPPostSimpleSignDecoder
+     */
+    @Bean(name = "openHTTPPostSimpleSignDecoder")
+    public OpenHTTPPostSimpleSignDecoder openHTTPPostSimpleSignDecoder(BasicParserPool samlParserPool,
+            @Value("${config.saml.v20.idp.receiver.endpoint}") String receiverEndpoint) {
+        OpenHTTPPostSimpleSignDecoder decoder = new OpenHTTPPostSimpleSignDecoder(samlParserPool);
+        decoder.setReceiverEndpoint(receiverEndpoint);
+        return decoder;
+    }
+    
+    /**
+     * OpenHTTPPostDecoder.
+     * @return openHTTPPostDecoder
+     */
+    @Bean(name = "openHTTPPostDecoder")
+    public OpenHTTPPostDecoder openHTTPPostDecoder(BasicParserPool samlParserPool,
+            @Value("${config.saml.v20.idp.receiver.endpoint}") String receiverEndpoint) {
+        OpenHTTPPostDecoder decoder = new OpenHTTPPostDecoder(samlParserPool);
+        decoder.setReceiverEndpoint(receiverEndpoint);
+        return decoder;
+    }
+    
+    /**
+     * OpenHTTPRedirectDecoder.
+     * @return openHTTPRedirectDecoder
+     */
+    @Bean(name = "openHTTPRedirectDecoder")
+    public OpenHTTPRedirectDecoder openHTTPRedirectDecoder(BasicParserPool samlParserPool,
+            @Value("${config.saml.v20.idp.receiver.endpoint}") String receiverEndpoint) {
+        OpenHTTPRedirectDecoder decoder = new OpenHTTPRedirectDecoder(samlParserPool);
+        decoder.setReceiverEndpoint(receiverEndpoint);
+        return decoder;
+    }
+   
+    /**
+     * ExtractPostBindingAdapter.
+     * @return extractPostBindingAdapter
+     */
+    @Bean(name = "extractPostBindingAdapter")
+    public ExtractPostBindingAdapter extractPostBindingAdapter(OpenHTTPPostDecoder openHTTPPostDecoder,
+            KeyStoreLoader keyStoreLoader,IssueInstantRule issueInstantRule,MessageReplayRule messageReplayRule) {
+        ExtractPostBindingAdapter adapter = new ExtractPostBindingAdapter(openHTTPPostDecoder);
+        adapter.setIssueInstantRule(issueInstantRule);
+        adapter.setKeyStoreLoader(keyStoreLoader);
+        adapter.setMessageReplayRule(messageReplayRule);
+        return adapter;
+    }
+    
+    /**
+     * ExtractRedirectBindingAdapter.
+     * @return extractRedirectBindingAdapter
+     */
+    @Bean(name = "extractRedirectBindingAdapter")
+    public ExtractRedirectBindingAdapter extractRedirectBindingAdapter(OpenHTTPRedirectDecoder openHTTPRedirectDecoder,
+            KeyStoreLoader keyStoreLoader,IssueInstantRule issueInstantRule,MessageReplayRule messageReplayRule) {
+        ExtractRedirectBindingAdapter adapter = new ExtractRedirectBindingAdapter(openHTTPRedirectDecoder);
+        adapter.setIssueInstantRule(issueInstantRule);
+        adapter.setKeyStoreLoader(keyStoreLoader);
+        adapter.setMessageReplayRule(messageReplayRule);
+        return adapter;
+    }
+    
+    
+    /**
+     * PostSimpleSignBindingAdapter.
+     * @return postSimpleSignBindingAdapter
+     */
+    @Bean(name = "postSimpleSignBindingAdapter")
+    public PostSimpleSignBindingAdapter postSimpleSignBindingAdapter(VelocityEngine velocityEngine,
+            @Value("${config.saml.v20.idp.issuer}") String issuerEntityName) {
+        PostSimpleSignBindingAdapter adapter = new PostSimpleSignBindingAdapter();
+        adapter.setVelocityEngine(velocityEngine);
+        adapter.setIssuerEntityName(issuerEntityName);
+        return adapter;
+    }
+    
+    /**
+     * PostBindingAdapter.
+     * @return postBindingAdapter
+     */
+    @Bean(name = "postBindingAdapter")
+    public PostBindingAdapter postBindingAdapter(VelocityEngine velocityEngine,
+            @Value("${config.saml.v20.idp.issuer}") String issuerEntityName) {
+        PostBindingAdapter adapter = new PostBindingAdapter();
+        adapter.setVelocityEngine(velocityEngine);
+        adapter.setIssuerEntityName(issuerEntityName);
+        return adapter;
+    }
+    
+    
+    
+   
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        // TODO Auto-generated method stub
+        
+    }
+}
diff --git a/maxkey-web-maxkey/src/main/resources/META-INF/spring.factories b/maxkey-web-maxkey/src/main/resources/META-INF/spring.factories
index b375262a3..7f156f262 100644
--- a/maxkey-web-maxkey/src/main/resources/META-INF/spring.factories
+++ b/maxkey-web-maxkey/src/main/resources/META-INF/spring.factories
@@ -3,4 +3,7 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
 org.maxkey.autoconfigure.ApplicationAutoConfiguration,\
 org.maxkey.autoconfigure.MvcAutoConfiguration,\
 org.maxkey.autoconfigure.KaptchaAutoConfiguration,\
-org.maxkey.autoconfigure.RedisAutoConfiguration
+org.maxkey.autoconfigure.RedisAutoConfiguration,\
+org.maxkey.autoconfigure.CasAutoConfiguration,\
+org.maxkey.autoconfigure.Oauth20AutoConfiguration,\
+org.maxkey.autoconfigure.Saml20AutoConfiguration
diff --git a/maxkey-web-maxkey/src/main/resources/spring/maxkey-protocol-cas.xml b/maxkey-web-maxkey/src/main/resources/spring/maxkey-protocol-cas.xml
deleted file mode 100644
index e322e2196..000000000
--- a/maxkey-web-maxkey/src/main/resources/spring/maxkey-protocol-cas.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-
- 
-		
-			
-		
-		
-		 
-
\ No newline at end of file
diff --git a/maxkey-web-maxkey/src/main/resources/spring/maxkey-protocol-oauth2.0.xml b/maxkey-web-maxkey/src/main/resources/spring/maxkey-protocol-oauth2.0.xml
deleted file mode 100644
index fab754b10..000000000
--- a/maxkey-web-maxkey/src/main/resources/spring/maxkey-protocol-oauth2.0.xml
+++ /dev/null
@@ -1,135 +0,0 @@
-
-
-
-	
-	
-	
-	
-	
-	 
-	
-	
-	
-	
-		
-		
-		
-		
-	
-	
-	
-		
-		
-		
-		
-	
-	
-	
-		
-	
-	
-	
-		
-		
- 		
-	
-
-	
-		
-		
-		
-		
-	
-	
-	
-	
-	
-	
-	
-	
-	
-	
-	
-	 
-	
-	
-	
-	
-	
-	
-		 
-		
-	
-	
-	
-		
-		
-	
-	
-	
-	
-		
-		
-		 
-		
-	
-	
-	
-		
-	
-	
-	
-		
-	
-	
-	
-		
-		
-		
-	
-	
-	  
-        
-              
-                 
-                	
-                		
-                	 
-        			  
-    			
-              
-       
-      
-    
-
\ No newline at end of file
diff --git a/maxkey-web-maxkey/src/main/resources/spring/maxkey-protocol-saml.xml b/maxkey-web-maxkey/src/main/resources/spring/maxkey-protocol-saml.xml
deleted file mode 100644
index 5a19b1664..000000000
--- a/maxkey-web-maxkey/src/main/resources/spring/maxkey-protocol-saml.xml
+++ /dev/null
@@ -1,153 +0,0 @@
-
-
-
-	
-	
-	
-	
-	
-	
-	
-	
-	
-	
-	
-	
-
-	
-	   
-	   
-		
-			classpath
-			org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader
-		
-		
-	
-	
-	
-	
-	
-		
-		
-	 
-	
-	
-		
-	
-		
-	
-			 
-	
-	
-	
-	
-			
-			
-	
-	
-	
-			
-			
-	
-	
-	
-			
-			
-	
-
-
-	
-		
-		
-		
-		
-	
-	
-	
-	
-	
-		
-		
-		
-		
-	
-	
-	
-	
-		
-		
-		
-		
-	
-	
-	
-	
-		
-		
-	
-	
-	
-	
-		
-		
-	
-	
-	
-		
-	
-	
-	
-	
-	
-		
-		
-		
-	
-	
-	
-    	
-		 
-		
-		
-	
-	
-	
-    	
-		 
-		
-	
-	
-	
-		
-	
-	
-	
-	
-	
-
diff --git a/maxkey-web-maxkey/src/main/resources/spring/maxkey-support-basic.xml b/maxkey-web-maxkey/src/main/resources/spring/maxkey-support-basic.xml
deleted file mode 100644
index a8b46b9cc..000000000
--- a/maxkey-web-maxkey/src/main/resources/spring/maxkey-support-basic.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-	
-	
-	
-		
-			
-			
-				
-			
-			
-	
-
-
\ No newline at end of file
diff --git a/maxkey-web-maxkey/src/main/resources/spring/maxkey-support-httpheader.xml b/maxkey-web-maxkey/src/main/resources/spring/maxkey-support-httpheader.xml
deleted file mode 100644
index 2029460db..000000000
--- a/maxkey-web-maxkey/src/main/resources/spring/maxkey-support-httpheader.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
- 
-
-	
-		
-		
-	
-	
-	
-	
-		
-			
-			
-			
-	
-
-
\ No newline at end of file
diff --git a/maxkey-web-maxkey/src/main/resources/spring/maxkey-support-kerberos.xml b/maxkey-web-maxkey/src/main/resources/spring/maxkey-support-kerberos.xml
deleted file mode 100644
index 0466185f7..000000000
--- a/maxkey-web-maxkey/src/main/resources/spring/maxkey-support-kerberos.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
-  
-	
-	
-		
-			
-				
-				
-					
-					
-					
-					
-				
-			
-		
-	
- 	
-
\ No newline at end of file
diff --git a/maxkey-web-maxkey/src/main/resources/spring/maxkey-support-social.xml b/maxkey-web-maxkey/src/main/resources/spring/maxkey-support-social.xml
deleted file mode 100644
index eabfbbfc7..000000000
--- a/maxkey-web-maxkey/src/main/resources/spring/maxkey-support-social.xml
+++ /dev/null
@@ -1,97 +0,0 @@
-
-
-
-	
-	
-	
-	
-	
-		
-		
-		
-		
-		
-		
-	
-	
-	
-		
-		
-		
-		
-		
-		
-	
-	
-	
-		
-		
-		
-		
-		
-		
-	
-
-	
-		
-		
-		
-		
-		
-		
-	
-
-	
-		
-		
-		
-		
-		
-		
-		
-	
-		
-		
-		
-		
-		
-		
-		
-	
-	
-		
-			
-				
-				
-				
-				
-				
-				
-				
-			
-		
-	
-	
-	
-	
-		
-	
-	
-
\ No newline at end of file
diff --git a/maxkey-web-maxkey/src/main/resources/spring/maxkey-support-wsfederation.xml b/maxkey-web-maxkey/src/main/resources/spring/maxkey-support-wsfederation.xml
deleted file mode 100644
index 473b8d5cc..000000000
--- a/maxkey-web-maxkey/src/main/resources/spring/maxkey-support-wsfederation.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-
-
- 
-	
-    
-        
-        
-        
-        
-        
-        
-        
-        
-        
-            
-        
-        
-            
-                classpath:${config.support.wsfederation.signingCertificate}
-            
-        
-    
-    
-    
-    	 
-    
-    
-	
-
-
\ No newline at end of file
diff --git a/maxkey-web-maxkey/src/main/resources/spring/maxkey.xml b/maxkey-web-maxkey/src/main/resources/spring/maxkey.xml
index 1016124cc..2a9d24982 100644
--- a/maxkey-web-maxkey/src/main/resources/spring/maxkey.xml
+++ b/maxkey-web-maxkey/src/main/resources/spring/maxkey.xml
@@ -18,30 +18,160 @@
 	        http://www.springframework.org/schema/mvc 
 	        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
 	        
-	
-	
-    
-    
-    
-    
-    
+	
+    
+        
+        
+    
+    
+    
+        
+            
+            
+          
+    
+    
+    
+    
+    
+        
+            
+            
+                
+            
+          
+    
+    
+    
+    
+    
+        
+            
+                
+                
+                    
+                    
+                    
+                    
+                
+            
+        
+    
+    
+    
+    
+    
+    
- 	
-    
-    
-    
-    
-    
-    
+    
+         
+    
+     -->
+    
+    
+    
+    
+    
+    
+    
+    
+        
+        
+        
+        
+        
+        
+    
+    
+    
+        
+        
+        
+        
+        
+        
+    
+    
+    
+        
+        
+        
+        
+        
+        
+    
 
-    
-    
-    
+    
+        
+        
+        
+        
+        
+        
+    
+
+    
+        
+        
+        
+        
+        
+        
+     
+    
+        
+        
+        
+        
+        
+        
+     
     
-	
+    
+        
+            
+                
+                
+                
+                
+                
+                
+                
+            
+        
+    
+    
+    
+        
+    
+     
+     
  	
     
     
@@ -95,8 +225,6 @@
             
             
         
-
-        
         
     
 
diff --git a/maxkey-web-maxkey/src/main/resources/templates/views/authorize/app_password_protected.ftl b/maxkey-web-maxkey/src/main/resources/templates/views/authorize/app_password_protected.ftl
index 5293069a9..650875ff4 100644
--- a/maxkey-web-maxkey/src/main/resources/templates/views/authorize/app_password_protected.ftl
+++ b/maxkey-web-maxkey/src/main/resources/templates/views/authorize/app_password_protected.ftl
@@ -1,18 +1,11 @@
 
 
 
-  	
-  	Second Protected
- 	
-  	
-	
-
-  	
-
+    <#include  "formbased_common.ftl">
 
 
 
-	
+ diff --git a/maxkey-web-maxkey/src/main/resources/templates/views/authorize/desktop_qq_sso_execute.ftl b/maxkey-web-maxkey/src/main/resources/templates/views/authorize/desktop_qq_sso_execute.ftl index d081bfc1b..ae3b80862 100644 --- a/maxkey-web-maxkey/src/main/resources/templates/views/authorize/desktop_qq_sso_execute.ftl +++ b/maxkey-web-maxkey/src/main/resources/templates/views/authorize/desktop_qq_sso_execute.ftl @@ -1,10 +1,7 @@ - - QQ SSO Execute - - + <#include "formbased_common.ftl"> - + <#include "formbased_common.ftl"> + + + + + + \ No newline at end of file diff --git a/maxkey-web-maxkey/src/main/resources/templates/views/authorize/formbased_qq_exmail_sso_submint.ftl b/maxkey-web-maxkey/src/main/resources/templates/views/authorize/formbased_qq_exmail_sso_submint.ftl index 0b963c58c..f1d3ee4f7 100644 --- a/maxkey-web-maxkey/src/main/resources/templates/views/authorize/formbased_qq_exmail_sso_submint.ftl +++ b/maxkey-web-maxkey/src/main/resources/templates/views/authorize/formbased_qq_exmail_sso_submint.ftl @@ -1,18 +1,7 @@ - - Form-Based SSO Submit - - - - - - - - - - + <#include "formbased_common.ftl">
<@locale code="userinfo.appLoginPassword" />