This project can be run in two ways to start: first,use the run debug Main.java: debug start, and then redirect loop;Second, debug by spring boot run commandrun (use the spring-boot-maven-plugin startup),but no redirection loop. The environment is those:spring-boot-starter-parent :1.2.3.RELEASE. How the whole?
/*--------- FreeMarker Configuration -------**/
#Bean
public FreeMarkerConfigurer freeMarkerConfigurer() {
FreeMarkerConfigurer fmc = new FreeMarkerConfigurer();
fmc.setPreferFileSystemAccess(false);
fmc.setTemplateLoaderPath("/WEB-INF/templates");
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("base", contextPath);
fmc.setFreemarkerVariables(variables);
Properties settings = new Properties();
settings.put("defaultEncoding","UTF-8");
fmc.setFreemarkerSettings(settings);
return fmc;
}
#Bean
public FreeMarkerViewResolver freeMarkerViewResolver() {
FreeMarkerViewResolver fvr = new FreeMarkerViewResolver();
fvr.setCache(false);
fvr.setPrefix("");
fvr.setSuffix(".ftl");
fvr.setContentType("text/html;charset=UTF-8");
return fvr;
}
/*--------- Shiro Configuration -------**/
private static Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
#Bean(name = "ShiroRealmImpl")
public ShiroDatabaseRealm getShiroRealm() {
return new ShiroDatabaseRealm();
}
#Bean(name = "securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager() {
DefaultWebSecurityManager dwsm = new DefaultWebSecurityManager();
dwsm.setRealm(getShiroRealm());
dwsm.setCacheManager(getEhCacheManager());
return dwsm;
}
#Bean(name = "shiroEhcacheManager")
public EhCacheManager getEhCacheManager() {
EhCacheManager em = new EhCacheManager();
em.setCacheManagerConfigFile("classpath:ehcache-shiro.xml");
return em;
}
#Bean(name = "lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
/**
*
* #return
*/
#Bean
public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor() {
AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor();
aasa.setSecurityManager(getDefaultWebSecurityManager());
return new AuthorizationAttributeSourceAdvisor();
}
#Bean(name = "shiroFilter")
public ShiroFilterFactoryBean shiroFilter() {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(getDefaultWebSecurityManager());
shiroFilterFactoryBean.setLoginUrl("/initlogin");
shiroFilterFactoryBean.setUnauthorizedUrl("/login.jhtml");
shiroFilterFactoryBean.setSuccessUrl("/admin/common/main");
filterChainDefinitionMap.put("/admin/**", "authc");
filterChainDefinitionMap.put("/api/**", "anon");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
/*--------- Mapping Handler -------**/
#RequestMapping(value={"/"})
private String home(){
return "redirect:/admin/common/main";
}
#RequestMapping(value={"/admin/common/main"}, method={RequestMethod.GET,RequestMethod.POST})
private String main(){
return "/admin/common/main";//main.ftl
}
#RequestMapping("/initlogin")
public String login(ModelMap model,HttpServletRequest request) {
boolean isAuth = SecurityUtils.getSubject().isAuthenticated();
if(isAuth){
return "redirect:/admin/common/main.jhtml";
}
return "login";
}
#RequestMapping(value={"/login"}, method={RequestMethod.GET,RequestMethod.POST})
public String login(String username,String password,ModelMap model){
boolean isAuth = SecurityUtils.getSubject().isAuthenticated();
if(!isAuth){//
String errorMsg = null;
try{
if(StringUtils.isEmpty(username) || StringUtils.isEmpty(password)){
errorMsg = "admin.login.incorrectCredentials";
model.addAttribute("msg", ApplicationSupport.getMessageByEnv(errorMsg));
}else{
UsernamePasswordToken token = new UsernamePasswordToken(username,DigestUtils.md5Hex(password));
if (token!=null)
SecurityUtils.getSubject().login(token);
}
}catch(Exception e){
return "login";
}
return "login";// I want to go redirect to login.ftl
}
//redirect to main.ftl
return "redirect:/admin/common/main.jhtml";
}
Related
I have created my authorization server using org.springframework.security:spring-security-oauth2-authorization-server:0.2.2 and my client using org.springframework.boot:spring-boot-starter-oauth2-client. The users are able to sign in and out successfully, however, while testing I noticed that if I log in successfully then restart the client (but not the server) without signing out and try to login in again the server throws the following error in an endless loop of redirects
java.lang.IllegalArgumentException: The class with org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken and name of org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken is not in the allowlist. If you believe this class is safe to deserialize, please provide an explicit mapping using Jackson annotations or by providing a Mixin. If the serialization is only done by a trusted source, you can also enable default typing. See https://github.com/spring-projects/spring-security/issues/4370 for details
I tried to follow this link https://github.com/spring-projects/spring-security/issues/4370 but the solution on it did not work for me. I also tried a different solution described in this link https://github.com/spring-projects/spring-authorization-server/issues/397#issuecomment-900148920 and modified my authorization server code as follows:-
Here is my Jackson Configs
#Configuration
public class JacksonConfiguration {
/**
* Support for Java date and time API.
*
* #return the corresponding Jackson module.
*/
#Bean
public JavaTimeModule javaTimeModule() {
return new JavaTimeModule();
}
#Bean
public Jdk8Module jdk8TimeModule() {
return new Jdk8Module();
}
/*
* Support for Hibernate types in Jackson.
*/
#Bean
public Hibernate5Module hibernate5Module() {
return new Hibernate5Module();
}
/*
* Module for serialization/deserialization of RFC7807 Problem.
*/
#Bean
public ProblemModule problemModule() {
return new ProblemModule();
}
/*
* Module for serialization/deserialization of ConstraintViolationProblem.
*/
#Bean
public ConstraintViolationProblemModule constraintViolationProblemModule() {
return new ConstraintViolationProblemModule();
}
/**
* To (de)serialize a BadCredentialsException, use CoreJackson2Module:
*/
#Bean
public CoreJackson2Module coreJackson2Module() {
return new CoreJackson2Module();
}
#Bean
#Primary
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(coreJackson2Module());
mapper.registerModule(javaTimeModule());
mapper.registerModule(jdk8TimeModule());
mapper.registerModule(hibernate5Module());
mapper.registerModule(problemModule());
mapper.registerModule(constraintViolationProblemModule());
return mapper;
}
}
and here is my Authorization server config
#Configuration(proxyBeanMethods = false)
public class AuthServerConfig {
private final DataSource dataSource;
private final AuthProperties authProps;
private final PasswordEncoder encoder;
public AuthServerConfig(DataSource dataSource, AuthProperties authProps, PasswordEncoder encoder) {
this.dataSource = dataSource;
this.authProps = authProps;
this.encoder = encoder;
}
#Bean
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(dataSource);
}
#Bean
#Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain authServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfigurer<HttpSecurity> authorizationServerConfigurer =
new OAuth2AuthorizationServerConfigurer<>();
authorizationServerConfigurer.tokenRevocationEndpoint(tokenRevocationEndpoint -> tokenRevocationEndpoint
.revocationResponseHandler((request, response, authentication) -> {
Assert.notNull(request, "HttpServletRequest required");
HttpSession session = request.getSession(false);
if (!Objects.isNull(session)) {
session.removeAttribute("SPRING_SECURITY_CONTEXT");
session.invalidate();
}
SecurityContextHolder.getContext().setAuthentication(null);
SecurityContextHolder.clearContext();
response.setStatus(HttpStatus.OK.value());
})
);
RequestMatcher endpointsMatcher = authorizationServerConfigurer.getEndpointsMatcher();
http
.requestMatcher(endpointsMatcher)
.authorizeRequests(authorizeRequests -> authorizeRequests.anyRequest().authenticated())
.csrf(csrf -> csrf.ignoringRequestMatchers(endpointsMatcher))
.apply(authorizationServerConfigurer);
return http.formLogin(Customizer.withDefaults()).build();
}
#Bean
public RegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTemplate, TokenSettings tokenSettings) {
JdbcRegisteredClientRepository clientRepository = new JdbcRegisteredClientRepository(jdbcTemplate);
RegisteredClient webClient = RegisteredClient.withId("98a9104c-a9c7-4d7c-ad03-ec61bcfeab36")
.clientId(authProps.getClientId())
.clientName(authProps.getClientName())
.clientSecret(encoder.encode(authProps.getClientSecret()))
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
.redirectUri("http://127.0.0.1:8000/login/oauth2/code/web-client")
.scope(OidcScopes.OPENID)
.scope(OidcScopes.PROFILE)
.tokenSettings(tokenSettings)
.build();
clientRepository.save(webClient);
return clientRepository;
}
#Bean
public OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate,
RegisteredClientRepository registeredClientRepository,
ObjectMapper objectMapper) {
JdbcOAuth2AuthorizationService authorizationService =
new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository);
JdbcOAuth2AuthorizationService.OAuth2AuthorizationRowMapper rowMapper = new JdbcOAuth2AuthorizationService.OAuth2AuthorizationRowMapper(registeredClientRepository);
ClassLoader classLoader = JdbcOAuth2AuthorizationService.class.getClassLoader();
objectMapper.registerModules(SecurityJackson2Modules.getModules(classLoader));
objectMapper.registerModule(new OAuth2AuthorizationServerJackson2Module());
// You will need to write the Mixin for your class so Jackson can marshall it.
// objectMapper.addMixIn(UserPrincipal .class, UserPrincipalMixin.class);
rowMapper.setObjectMapper(objectMapper);
authorizationService.setAuthorizationRowMapper(rowMapper);
return authorizationService;
}
#Bean
public OAuth2AuthorizationConsentService authorizationConsentService(JdbcTemplate jdbcTemplate,
RegisteredClientRepository registeredClientRepository) {
return new JdbcOAuth2AuthorizationConsentService(jdbcTemplate, registeredClientRepository);
}
#Bean
public JWKSource<SecurityContext> jwkSource() {
RSAKey rsaKey = generateRsa();
JWKSet jwkSet = new JWKSet(rsaKey);
return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
}
private static RSAKey generateRsa() {
KeyPair keyPair = generateRsaKey();
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
return new RSAKey.Builder(publicKey)
.privateKey(privateKey)
.keyID(UUID.randomUUID().toString())
.build();
}
private static KeyPair generateRsaKey() {
KeyPair keyPair;
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048);
keyPair = keyPairGenerator.generateKeyPair();
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
return keyPair;
}
#Bean
public ProviderSettings providerSettings() {
return ProviderSettings.builder()
.issuer(authProps.getIssuerUri())
.build();
}
#Bean
public TokenSettings tokenSettings() {
return TokenSettings.builder()
.accessTokenTimeToLive(Duration.ofDays(1))
.refreshTokenTimeToLive(Duration.ofDays(1))
.build();
}
}
But am still facing the same issue.
How do I solve this? Any assistance is highly appreciated.
After trying out different solutions this was how I was able to solve it.
I changed my OAuth2AuthorizationService bean to look like this.
#Bean
public OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate,
RegisteredClientRepository registeredClientRepository) {
JdbcOAuth2AuthorizationService authorizationService =
new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository);
JdbcOAuth2AuthorizationService.OAuth2AuthorizationRowMapper rowMapper =
new JdbcOAuth2AuthorizationService.OAuth2AuthorizationRowMapper(registeredClientRepository);
JdbcOAuth2AuthorizationService.OAuth2AuthorizationParametersMapper oAuth2AuthorizationParametersMapper =
new JdbcOAuth2AuthorizationService.OAuth2AuthorizationParametersMapper();
ObjectMapper objectMapper = new ObjectMapper();
ClassLoader classLoader = JdbcOAuth2AuthorizationService.class.getClassLoader();
List<Module> securityModules = SecurityJackson2Modules.getModules(classLoader);
objectMapper.registerModules(securityModules);
objectMapper.registerModule(new OAuth2AuthorizationServerJackson2Module());
objectMapper.addMixIn(JwtAuthenticationToken.class, JwtAuthenticationTokenMixin.class);
rowMapper.setObjectMapper(objectMapper);
oAuth2AuthorizationParametersMapper.setObjectMapper(objectMapper);
authorizationService.setAuthorizationRowMapper(rowMapper);
authorizationService.setAuthorizationParametersMapper(oAuth2AuthorizationParametersMapper);
return authorizationService;
}
and here is my JwtAuthenticationTokenMixin configurations
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
#JsonDeserialize(using = JwtAuthenticationTokenDeserializer.class)
#JsonAutoDetect(
fieldVisibility = JsonAutoDetect.Visibility.ANY,
getterVisibility = JsonAutoDetect.Visibility.NONE,
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
#JsonIgnoreProperties(ignoreUnknown = true)
public abstract class JwtAuthenticationTokenMixin {}
class JwtAuthenticationTokenDeserializer extends JsonDeserializer<JwtAuthenticationToken> {
#Override
public JwtAuthenticationToken deserialize(JsonParser parser, DeserializationContext context) throws IOException {
ObjectMapper mapper = (ObjectMapper) parser.getCodec();
JsonNode root = mapper.readTree(parser);
return deserialize(parser, mapper, root);
}
private JwtAuthenticationToken deserialize(JsonParser parser, ObjectMapper mapper, JsonNode root)
throws JsonParseException {
JsonNode principal = JsonNodeUtils.findObjectNode(root, "principal");
if (!Objects.isNull(principal)) {
String tokenValue = principal.get("tokenValue").textValue();
long issuedAt = principal.get("issuedAt").longValue();
long expiresAt = principal.get("expiresAt").longValue();
Map<String, Object> headers = JsonNodeUtils.findValue(
principal, "headers", JsonNodeUtils.STRING_OBJECT_MAP, mapper);
Map<String, Object> claims = new HashMap<>();
claims.put("claims", principal.get("claims"));
Jwt jwt = new Jwt(tokenValue, Instant.ofEpochMilli(issuedAt), Instant.ofEpochMilli(expiresAt), headers, claims);
return new JwtAuthenticationToken(jwt);
}
return null;
}
}
abstract class JsonNodeUtils {
static final TypeReference<Set<String>> STRING_SET = new TypeReference<Set<String>>() {
};
static final TypeReference<Map<String, Object>> STRING_OBJECT_MAP = new TypeReference<Map<String, Object>>() {
};
static String findStringValue(JsonNode jsonNode, String fieldName) {
if (jsonNode == null) {
return null;
}
JsonNode value = jsonNode.findValue(fieldName);
return (value != null && value.isTextual()) ? value.asText() : null;
}
static <T> T findValue(JsonNode jsonNode, String fieldName, TypeReference<T> valueTypeReference,
ObjectMapper mapper) {
if (jsonNode == null) {
return null;
}
JsonNode value = jsonNode.findValue(fieldName);
return (value != null && value.isContainerNode()) ? mapper.convertValue(value, valueTypeReference) : null;
}
static JsonNode findObjectNode(JsonNode jsonNode, String fieldName) {
if (jsonNode == null) {
return null;
}
JsonNode value = jsonNode.findValue(fieldName);
return (value != null && value.isObject()) ? value : null;
}
}
you don't need to create a Mixin, because it's all ready created by authorization springboot module. juste
#Bean
public OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {
JdbcOAuth2AuthorizationService authorizationService = new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository);
JdbcOAuth2AuthorizationService.OAuth2AuthorizationRowMapper rowMapper = new JdbcOAuth2AuthorizationService.OAuth2AuthorizationRowMapper(registeredClientRepository);
ClassLoader classLoader = JdbcOAuth2AuthorizationService.class.getClassLoader();
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModules(new CoreJackson2Module());
objectMapper.registerModules(SecurityJackson2Modules.getModules(classLoader));
objectMapper.registerModule(new OAuth2AuthorizationServerJackson2Module());
rowMapper.setObjectMapper(objectMapper);
authorizationService.setAuthorizationRowMapper(rowMapper);
return authorizationService;
}
i think you miss this line and is where the token mixin is registered
objectMapper.registerModules(new CoreJackson2Module());
What we are doing: Update subscription for received business ID.
We receive business ID on activeMQ queue.
JMSListener picked business ID, find the all associated subscription for received business.
Process all subscription identify who need update (can be zero or more than one).
For each subscription we call service method udate(SubscriptionDTO subscriptionDTO). Find the subscription entity object. This update method update the entity object using dto object. Then we call flush method on it. Note : Update method is annotated with #transactional. Call flush method from service#udpate(dto) method.
We observed different behavior in windows vs Linux. On windows it worked well but on Linux we are getting failure after execution of flush method– javax.persistence.TransactionRequiredException: no transaction is in progress
We tried adding #Transactional on JMSListner but got same error on Linux. But when we tried without call of flush method error gone but no update happened. Subscription data is same as before.
Not sure how good problem was explained above.
Looking for guidance, much appreciated for response.
JPA Configuration
package edu.learn.config;
#Configuration
#EnableJpaRepositories(basePackages = "edu.learn.repositories", entityManagerFactoryRef = "entityManagerFactory", transactionManagerRef = "transactionManager")
#EnableTransactionManagement
public class JpaConfiguration
{
private final String DEFAULT_TEST_QUERY = "SELECT 1";
#Autowired
private Environment environment;
#Value("${datasource.maxPoolSize:10}")
private int maxPoolSize;
/*
* Populate SpringBoot DataSourceProperties object directly from
* application.yml based on prefix.Thanks to .yml, Hierachical data is
* mapped out of the box with matching-name properties of
* DataSourceProperties object].
*/
#Bean
#Primary
#ConfigurationProperties(prefix = "datasource")
public DataSourceProperties dataSourceProperties()
{
return new DataSourceProperties();
}
/*
* Configure HikariCP pooled DataSource.
*/
#Bean
public DataSource dataSource()
{
DataSourceProperties dataSourceProperties = dataSourceProperties();
HikariDataSource dataSource = (HikariDataSource) DataSourceBuilder.create()
.type(com.zaxxer.hikari.HikariDataSource.class).username(dataSourceProperties.getUsername())
.password(dataSourceProperties.getPassword()).build();
dataSource.setMaximumPoolSize(PropertyUtil.getMaxPoolSize(environment));
dataSource.setInitializationFailFast(PropertyUtil.getInitializationFailFast(environment));
dataSource.setMinimumIdle(PropertyUtil.getMinIdle(environment));
dataSource.setConnectionTestQuery(DEFAULT_TEST_QUERY);
dataSource.setConnectionTimeout(PropertyUtil.getConnectionTimeout(environment));
dataSource.setIdleTimeout(PropertyUtil.getIdleTimeout(environment));
dataSource.setMaxLifetime(PropertyUtil.getMaxLifetime(environment));
dataSource.setDataSourceClassName(PropertyUtil.getDataSourceClassName(environment));
if ("com.mysql.jdbc.jdbc2.optional.MysqlDataSource".equals(PropertyUtil.getDataSourceClassName(environment)))
{
dataSource.addDataSourceProperty("databaseName", PropertyUtil.defaultSchema(environment));
dataSource.addDataSourceProperty("cachePrepStmts", PropertyUtil.getCachePrepStmts(environment));
dataSource.addDataSourceProperty("prepStmtCacheSize", PropertyUtil.getPrepStmtCacheSize(environment));
dataSource.addDataSourceProperty(
"prepStmtCacheSqlLimit", PropertyUtil.getPrepStmtCacheSqlLimit(environment)
);
dataSource.addDataSourceProperty("useServerPrepStmts", PropertyUtil.getUseServerPrepStmts(environment));
dataSource.addDataSourceProperty("serverName", PropertyUtil.dbServerName(environment));
dataSource.addDataSourceProperty("portNumber", PropertyUtil.portNumber(environment));
}
return dataSource;
}
/*
* Entity Manager Factory setup.
*/
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws NamingException
{
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(dataSource());
factoryBean.setPackagesToScan(new String[]
{
"edu.learn.model"
});
factoryBean.setJpaVendorAdapter(jpaVendorAdapter());
factoryBean.setJpaProperties(jpaProperties());
return factoryBean;
}
/*
* Provider specific adapter.
*/
#Bean
public JpaVendorAdapter jpaVendorAdapter()
{
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
return hibernateJpaVendorAdapter;
}
/*
* Here you can specify any provider specific properties.
*/
private Properties jpaProperties()
{
Properties properties = new Properties();
properties.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
properties.put("hibernate.globally_quoted_identifiers", "true");
properties.put("hibernate.hbm2ddl.auto", "validates");
properties.put("ejb.naming_strategy", "org.hibernate.cfg.ImprovedNamingStrategy");
properties.put("show_sql", "false");
properties.put("format_sql", "true");
return properties;
}
#Bean
#Autowired
public PlatformTransactionManager transactionManager(EntityManagerFactory emf)
{
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(emf);
return txManager;
}
static class PropertyUtil
{
final static String DEFAULT_CHARACTER_ENCODING = "utf-8";
final static String DEFAULT_DATASOURCE_CLASS = "com.mysql.jdbc.Driver";
public static boolean getCachePrepStmts(final Environment environment)
{
String cachePrepStmts = environment.getProperty("spring.datasource.hikari.cachePrepStmts");
if ("false".equalsIgnoreCase(cachePrepStmts))
{
return false;
}
return true;
}
public static int getPrepStmtCacheSize(final Environment environment)
{
String prepStmtCacheSize = environment.getProperty("spring.datasource.hikari.prepStmtCacheSize");
try
{
return Integer.parseInt(prepStmtCacheSize);
}
catch(Exception e)
{
return 250;
}
}
public static int getPrepStmtCacheSqlLimit(final Environment environment)
{
String prepStmtCacheSqlLimit = environment.getProperty("spring.datasource.hikari.prepStmtCacheSqlLimit");
try
{
return Integer.parseInt(prepStmtCacheSqlLimit);
}
catch(Exception e)
{
return 2048;
}
}
public static boolean getUseServerPrepStmts(final Environment environment)
{
String useServerPrepStmts = environment.getProperty("spring.datasource.hikari.useServerPrepStmts");
if ("false".equalsIgnoreCase(useServerPrepStmts))
{
return false;
}
return true;
}
public static boolean getInitializationFailFast(final Environment environment)
{
String initializationFailFast = environment.getProperty("spring.datasource.hikari.initializationFailFast");
if ("false".equalsIgnoreCase(initializationFailFast))
{
return false;
}
return true;
}
public static long getConnectionTimeout(final Environment environment)
{
String connectionTimeout = environment.getProperty("spring.datasource.hikari.connectionTimeout");
try
{
return Integer.parseInt(connectionTimeout);
}
catch(Exception e)
{
return TimeUnit.SECONDS.toMillis(60);
}
}
public static long getIdleTimeout(final Environment environment)
{
String idleTimeout = environment.getProperty("spring.datasource.hikari.idleTimeout");
try
{
return Integer.parseInt(idleTimeout);
}
catch(Exception e)
{
return TimeUnit.SECONDS.toMillis(60);
}
}
public static long getMaxLifetime(final Environment environment)
{
String maxLifetime = environment.getProperty("spring.datasource.hikari.maxLifetime");
try
{
return Integer.parseInt(maxLifetime);
}
catch(Exception e)
{
return TimeUnit.SECONDS.toMillis(90);
}
}
public static int getMinIdle(final Environment environment)
{
String minIdle = environment.getProperty("spring.datasource.hikari.minIdle");
try
{
return Integer.parseInt(minIdle);
}
catch(Exception e)
{
return 5;
}
}
public static int getMaxPoolSize(final Environment environment)
{
String maxPoolSize = environment.getProperty("spring.datasource.maxPoolSize");
try
{
return Integer.parseInt(maxPoolSize);
}
catch(Exception e)
{
return 25;
}
}
public static String getDataSourceClassName(final Environment environment)
{
String dataSourceClassName = environment.getProperty("spring.datasource.dataSourceClassName");
if (dataSourceClassName != null && "".equalsIgnoreCase(dataSourceClassName.trim()) == false)
{
return dataSourceClassName;
}
return DEFAULT_DATASOURCE_CLASS;
}
public static String getCharacterEncoding(final Environment environment)
{
String characterEncoding = environment.getProperty("spring.datasource.characterEncoding");
if (characterEncoding != null && "".equalsIgnoreCase(characterEncoding.trim()) == false)
{
return characterEncoding;
}
return DEFAULT_CHARACTER_ENCODING;
}
public static boolean getUniCode(final Environment environment)
{
String useUnicode = environment.getProperty("spring.datasource.useUnicode");
if ("false".equalsIgnoreCase(useUnicode))
{
return false;
}
return true;
}
public static String showSQL(final Environment environment)
{
String showSQL = environment.getProperty("spring.datasource.hibernate.showSQL");
if ("false".equalsIgnoreCase(showSQL))
{
return "false";
}
return "true";
}
public static String formatSQL(final Environment environment)
{
String formatSQL = environment.getProperty("spring.datasource.hibernate.format_sql");
if ("false".equalsIgnoreCase(formatSQL))
{
return "false";
}
return "true";
}
public static String dbServerName(final Environment environment)
{
String dbServerName = environment.getProperty("spring.datasource.serverName");
if (dbServerName == null || "".equalsIgnoreCase(dbServerName.trim()))
{
return "localhost";
}
return dbServerName;
}
public static int portNumber(final Environment environment)
{
String portNumber = environment.getProperty("spring.datasource.portNumber");
try
{
return Integer.parseInt(portNumber);
}
catch(Exception e)
{
return 3306;
}
}
public static String defaultSchema(final Environment environment)
{
String defaultSchema = environment.getProperty("spring.datasource.defaultSchema");
if (defaultSchema == null || "".equalsIgnoreCase(defaultSchema.trim()))
{
return "subscription";
}
return defaultSchema;
}
}
}
JMS Configuration
#EnableJms
#Configuration
public class JmsConfiguration
{
#Bean
public JmsListenerContainerFactory<?> myFactory(ConnectionFactory connectionFactory,
DefaultJmsListenerContainerFactoryConfigurer configurer)
{
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
configurer.configure(factory, connectionFactory);
return factory;
}
}
Startup class
#Import({JpaConfiguration.class, JmsConfiguration.class})
#SpringBootApplication(scanBasePackages={"edu.learn"})
public class SubscriptionApiApplication
{
public static void main(String[] args)
{
SpringApplication.run(SubscriptionApiApplication.class, args);
}
}
JMS queue listener
#Component
public class OrderTransactionReceiver
{
private static final Logger LOGGER = LoggerFactory.getLogger(OrderTransactionReceiver.class);
#Autowired
private SubscriptionService subsriptionService;
#JmsListener(destination = "OrderTransactionQueue", containerFactory = "myFactory")
public void receiveMessage(String businessID)
{
List<SubscriptionDTO> subscriptions = subsriptionService.findByBusinessID(businessID);
for (SubscriptionDTO subscription : subscriptions)
{
subscription.setStatus("active");
subsriptionService.update(subscription);
}
}
}
Service
#Service
class RepositorySubscriptionService implements SubscriptionService
{
private static final Logger LOGGER = LoggerFactory.getLogger(RepositorySubscriptionService.class);
private final SubscriptionRepository repository;
#Transactional
#Override
public SubscriptionDTO update(SubscriptionDTO updatedSubscriptionEntry)
{
LOGGER.info(
"Updating the information of a subscription entry by using information: {}", updatedSubscriptionEntry
);
SubscriptionEntity updated = findSubscriptionEntryById(updatedSubscriptionEntry.getId());
updated.update(updatedSubscriptionEntry.getStatus());
// We need to flush the changes or otherwise the returned object
// doesn't contain the updated audit information.
repository.flush();
LOGGER.info("Updated the information of the subscription entry: {}", updated);
return SubscriptionMapper.mapEntityIntoDTO(updated);
}
}
Repostiory
public interface SubscriptionRepository extends
Repository<SubscriptionEntity, String>,
JpaSpecificationExecutor<SubscriptionEntity>
{
void delete(SubscriptionEntity deleted);
List<SubscriptionEntity> findAll();
Optional<SubscriptionEntity> findOne(String id);
List<SubscriptionEntity> findByBusinessID(final String businessID);
void flush();
SubscriptionEntity save(SubscriptionEntity persisted);
}
JTA transaction configuration
#Configuration
#EnableJpaRepositories(basePackages = "edu.learn.repositories", entityManagerFactoryRef = "entityManagerFactory", transactionManagerRef = "transactionManager")
#EnableTransactionManagement
public class JpaConfiguration
{
private final String DEFAULT_TEST_QUERY = "SELECT 1";
#Autowired
private Environment environment;
#Value("${datasource.maxPoolSize:10}")
private int maxPoolSize;
/*
* Populate SpringBoot DataSourceProperties object directly from
* application.yml based on prefix.Thanks to .yml, Hierachical data is
* mapped out of the box with matching-name properties of
* DataSourceProperties object].
*/
#Bean
#Primary
#ConfigurationProperties(prefix = "datasource")
public DataSourceProperties dataSourceProperties()
{
return new DataSourceProperties();
}
/*
* Configure HikariCP pooled DataSource.
*/
#Bean(initMethod = "init", destroyMethod = "close")
public DataSource dataSource()
{
DataSourceProperties dataSourceProperties = dataSourceProperties();
MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
mysqlXaDataSource.setServerName(PropertyUtil.dbServerName(environment));
mysqlXaDataSource.setPort(PropertyUtil.portNumber(environment));
mysqlXaDataSource.setUser(dataSourceProperties.getUsername());
mysqlXaDataSource.setPassword(dataSourceProperties.getPassword());
mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
mysqlXaDataSource.setDatabaseName(PropertyUtil.defaultSchema(environment));
mysqlXaDataSource.setCachePreparedStatements(PropertyUtil.getCachePrepStmts(environment));
try
{
mysqlXaDataSource.setPrepStmtCacheSqlLimit(PropertyUtil.getPrepStmtCacheSqlLimit(environment));
mysqlXaDataSource.setPrepStmtCacheSize(PropertyUtil.getPrepStmtCacheSize(environment));
}
catch (SQLException e)
{
e.printStackTrace();
}
AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
xaDataSource.setXaDataSource(mysqlXaDataSource);
xaDataSource.setUniqueResourceName("xaSubscription");
xaDataSource.setTestQuery(DEFAULT_TEST_QUERY);
xaDataSource.setMaxPoolSize(PropertyUtil.getMaxPoolSize(environment));
xaDataSource.setMaxIdleTime(PropertyUtil.getMinIdle(environment));
xaDataSource.setMaxLifetime((int)PropertyUtil.getMaxLifetime(environment));
xaDataSource.setBorrowConnectionTimeout((int)PropertyUtil.getConnectionTimeout(environment));
return xaDataSource;
}
/*
* Entity Manager Factory setup.
*/
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws NamingException
{
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(dataSource());
factoryBean.setPackagesToScan(new String[]
{
"edu.learn.model"
});
factoryBean.setJpaVendorAdapter(jpaVendorAdapter());
factoryBean.setJpaProperties(jpaProperties());
return factoryBean;
}
/*
* Provider specific adapter.
*/
#Bean
public JpaVendorAdapter jpaVendorAdapter()
{
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
return hibernateJpaVendorAdapter;
}
/*
* Here you can specify any provider specific properties.
*/
private Properties jpaProperties()
{
Properties properties = new Properties();
properties.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
properties.put("hibernate.globally_quoted_identifiers", "true");
properties.put("hibernate.hbm2ddl.auto", "validates");
properties.put("ejb.naming_strategy", "org.hibernate.cfg.ImprovedNamingStrategy");
properties.put("show_sql", "false");
properties.put("format_sql", "true");
properties.put("hibernate.transaction.jta.platform", AtomikosJtaPlatform.class.getName());
properties.put("javax.persistence.transactionType", "JTA");
return properties;
}
#Bean
public UserTransaction userTransaction() throws Throwable
{
UserTransactionImp userTransactionImp = new UserTransactionImp();
userTransactionImp.setTransactionTimeout(1000);
return userTransactionImp;
}
#Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close")
public TransactionManager atomikosTransactionManager() throws Throwable
{
UserTransactionManager userTransactionManager = new UserTransactionManager();
userTransactionManager.setForceShutdown(false);
AtomikosJtaPlatform.transactionManager = userTransactionManager;
return userTransactionManager;
}
#Bean(name = "transactionManager")
#DependsOn({ "userTransaction", "atomikosTransactionManager" })
public PlatformTransactionManager transactionManager() throws Throwable
{
UserTransaction userTransaction = userTransaction();
AtomikosJtaPlatform.transaction = userTransaction;
TransactionManager atomikosTransactionManager = atomikosTransactionManager();
return new JtaTransactionManager(userTransaction, atomikosTransactionManager);
}
}
public class AtomikosJtaPlatform extends AbstractJtaPlatform
{
private static final long serialVersionUID = 1L;
static TransactionManager transactionManager;
static UserTransaction transaction;
#Override
protected TransactionManager locateTransactionManager()
{
return transactionManager;
}
#Override
protected UserTransaction locateUserTransaction()
{
return transaction;
}
}
I test the shiro with springboot,but no matter what urls like 127.0.0.1:8080/index are redirected to the the UnauthorizedUrl("/error");
Here is my ShiroConfig:
#Configuration
public class ShiroConfig {
#Bean(name = "lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
#Bean
#DependsOn("lifecycleBeanPostProcessor")
public MyShiroRealm myShiroRealm(){
MyShiroRealm myShiroRealm = new MyShiroRealm();
myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return myShiroRealm;
}
#Bean
public HashedCredentialsMatcher hashedCredentialsMatcher(){
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("MD5");
hashedCredentialsMatcher.setHashIterations(2);
return hashedCredentialsMatcher;
}
#Bean
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm());
//securityManager.setCacheManager(ehCacheManager());
return securityManager;
}
#Bean
public ShiroFilterFactoryBean shiroFilter() {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager());
Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();
filterChainDefinitionMap.put("/index", "anon");
filterChainDefinitionMap.put("/logout", "logout");
filterChainDefinitionMap.put("/**", "authc");
shiroFilterFactoryBean.setLoginUrl("/login");
shiroFilterFactoryBean.setSuccessUrl("/welcome");
//Unauthorized;
shiroFilterFactoryBean.setUnauthorizedUrl("/error");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
#Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(){
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
return authorizationAttributeSourceAdvisor;
}
#Bean
#ConditionalOnMissingBean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
defaultAAP.setProxyTargetClass(true);
return defaultAAP;
}
#Bean
public PassThruAuthenticationFilter passThruAuthenticationFilter(){
return new PassThruAuthenticationFilter();
}
}
And the Realm
public class MyShiroRealm extends AuthorizingRealm {
private static final Logger LOGGER = Logger.getLogger(MyShiroRealm.class);
#Resource
UserService userService;
#Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
LOGGER.info("AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) : "+principalCollection);
String principal=(String) principalCollection.getPrimaryPrincipal();
LOGGER.info(principal);
User user=(User) userService.findUserByName(principal);
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
for(Role role:user.getRoles()){
info.addRole(role.getName());
for(Permission permission:role.getPermissions()){
info.addStringPermission(permission.getName());
}
}
return info;
}
#Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
LOGGER.info("AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) : "+authenticationToken);
String name=((UsernamePasswordToken)authenticationToken).getUsername();
User user=userService.findUserByName(name);
if (user != null) {
Session session = SecurityUtils.getSubject().getSession();
session.setAttribute("user", user);
return new SimpleAuthenticationInfo(name,user.getPassword(),getName());
} else {
return null;
}
}
}
Properties in application.properties
#thymeleaf
spring.thymeleaf.cache=false
#hibernate
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.show-sql= true
#html
spring.mvc.view.prefix=/
spring.mvc.view.suffix=.html
DB properties are not given. Tomcat's server port is 8080.
The recources dir:
dir of resource
When I access the "127.0.0.1:8080/index" ,it redirects to the "error" page setted by
shiroFilterFactoryBean.setUnauthorizedUrl("/error");.
when the "/login" Controller method is given it redirects to login page.
I am so confused and cannot find a way to solve it.
UPDATE
After moving the index.html to templates dir and make a #RequestMapping("/index") method,I get the index in browser.(If the index.html is in the static dir, A #RequestMapping("/index") method don't work.)
How could I get the static html ?
As I test, for the resources in static folder like "/css/**","/index.html" ,
If we want to access them without being intercepted by shiro,
full name like
"127.0.0.1:8080/css/a.css" or "127.0.0.1:8080/index.html",
should be used,
which the filterChainDefinitionMap should be setted as
filterChainDefinitionMap.put("/index.html", "anon");
filterChainDefinitionMap.put("/css/**", "anon");
I use spring boot 1.5.1 config the spring security acl without xml,but have some problem. My config java is:
The web security config class:
#Configuration
public class ACLConfig extends GlobalMethodSecurityConfiguration
{
#Autowired
DataSource dataSource;
#Bean(name = "aclCache")
public EhCacheBasedAclCache aclCache()
{
PermissionGrantingStrategy permissionGrantingStrategy = new DefaultPermissionGrantingStrategy(
new ConsoleAuditLogger());
return new EhCacheBasedAclCache(aclEhCache().getObject(),
permissionGrantingStrategy, aclAuthorizationStrategy());
}
#Bean(name = "aclEhCache")
public EhCacheFactoryBean aclEhCache()
{
EhCacheFactoryBean factoryBean = new EhCacheFactoryBean();
EhCacheManagerFactoryBean cacheManager = new EhCacheManagerFactoryBean();
cacheManager
.setConfigLocation(new ClassPathResource("cache/ehcache.xml"));
factoryBean.setCacheName("aclCache");
factoryBean.setCacheManager(cacheManager.getObject());
return factoryBean;
}
#Bean
public LookupStrategy lookupStrategy()
{
return new BasicLookupStrategy(dataSource, aclCache(),
aclAuthorizationStrategy(), new ConsoleAuditLogger());
}
#Bean(name = "adminRole")
public SimpleGrantedAuthority adminRole()
{
return new SimpleGrantedAuthority(Const.ADMIN_ROLE);
}
#Bean
public AclAuthorizationStrategy aclAuthorizationStrategy()
{
return new AclAuthorizationStrategyImpl(adminRole(), adminRole(),
adminRole());
}
#Bean(name = "aclService")
public MutableAclService aclService()
{
EhCacheBasedAclCache aclCache = aclCache();
JdbcMutableAclService service = new JdbcMutableAclService(dataSource,
lookupStrategy(), aclCache);
service.setClassIdentityQuery("select ##IDENTITY");
service.setSidIdentityQuery("select ##IDENTITY");
return service;
}
#Bean(name = "aclDeleteVoter")
public AclEntryVoter aclDeleteVoter()
{
String processConfigAttribute = Const.ACL_DELETE;
Permission[] requirePermission = new Permission[2];
requirePermission[0] = org.springframework.security.acls.domain.BasePermission.ADMINISTRATION;
requirePermission[1] = org.springframework.security.acls.domain.BasePermission.DELETE;
AclEntryVoter aclEntryVoter = new AclEntryVoter(aclService(),
processConfigAttribute, requirePermission);
// 设置要管理的class
aclEntryVoter.setProcessDomainObjectClass(AclDomainClass.class);
return aclEntryVoter;
}
#Override
protected AffirmativeBased accessDecisionManager()
{
return aclAccessDecisionManager();
}
#Bean(name = "aclAccessDecisionManager")
public AffirmativeBased aclAccessDecisionManager()
{
List<AccessDecisionVoter<? extends Object>> decisionVoters = new ArrayList<AccessDecisionVoter<? extends Object>>();
decisionVoters.add(new RoleVoter());
decisionVoters.add(aclDeleteVoter());
decisionVoters.add(afterAclRead());
decisionVoters.add(aclWriteVoter());
AffirmativeBased affirmativeBased = new AffirmativeBased(
decisionVoters);
return affirmativeBased;
}
#Bean(name = "afterAclRead")
public AclEntryVoter afterAclRead()
{
String processConfigAttribute = Const.ACL_READ;
Permission[] requirePermission = new Permission[2];
requirePermission[0] = org.springframework.security.acls.domain.BasePermission.ADMINISTRATION;
requirePermission[1] = org.springframework.security.acls.domain.BasePermission.READ;
AclEntryVoter aclEntryVoter = new AclEntryVoter(aclService(),
processConfigAttribute, requirePermission);
// 设置要管理的class
aclEntryVoter.setProcessDomainObjectClass(AclDomainClass.class);
return aclEntryVoter;
}
#Bean(name = "aclWriteVoter")
public AclEntryVoter aclWriteVoter()
{
String processConfigAttribute = Const.ACL_WRITE;
Permission[] requirePermission = new Permission[2];
requirePermission[0] = org.springframework.security.acls.domain.BasePermission.ADMINISTRATION;
requirePermission[1] = org.springframework.security.acls.domain.BasePermission.WRITE;
AclEntryVoter aclEntryVoter = new AclEntryVoter(aclService(),
processConfigAttribute, requirePermission);
// 设置要管理的class
aclEntryVoter.setProcessDomainObjectClass(AclDomainClass.class);
return aclEntryVoter;
}
#Override
protected MethodSecurityExpressionHandler createExpressionHandler()
{
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
expressionHandler.setPermissionEvaluator(
new AclPermissionEvaluator(aclService()));
return expressionHandler;
}
}
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true ,securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
}
#Secured(Const.ACL_DELETE)
public void delete(User user)
{
// TODO Auto-generated method stub
userMapper.delete(user);
ObjectIdentity oid = new ObjectIdentityImpl(User.class,
user.getId());
aclService.deleteAcl(oid, false);
}
but I visit this method throw a exception (org.springframework.security.access.AccessDeniedException);
I want to authenticate a user using openAm and share the session to communicate to other microservices without authenticating the user again. Session details are stored in an external database. I am using spring security saml sample application with all the java configuration to achieve this.
There are two sessions being generated . One before logging to the IDP and one after entering the credentials. If I check at openAm, user is logged in but in the application, i am getting SAMLException specifying InResponseToField of the Response doesnt correspond to sent message 12345(random id).
How to integrate spring session with saml?
Security config file of my sample application
package com.vdenotaris.spring.boot.security.saml.web.config;
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private SAMLUserDetailsServiceImpl samlUserDetailsServiceImpl;
#Bean
public VelocityEngine velocityEngine() {
return VelocityFactory.getEngine();
}
#Bean(initMethod = "initialize")
public StaticBasicParserPool parserPool() {
return new StaticBasicParserPool();
}
#Bean(name = "parserPoolHolder")
public ParserPoolHolder parserPoolHolder() {
return new ParserPoolHolder();
}
#Bean
public MultiThreadedHttpConnectionManager multiThreadedHttpConnectionManager() {
return new MultiThreadedHttpConnectionManager();
}
#Bean
public HttpClient httpClient() {
return new HttpClient(multiThreadedHttpConnectionManager());
}
#Bean
public SAMLAuthenticationProvider samlAuthenticationProvider() {
SAMLAuthenticationProvider samlAuthenticationProvider = new SAMLAuthenticationProvider();
samlAuthenticationProvider.setUserDetails(samlUserDetailsServiceImpl);
samlAuthenticationProvider.setForcePrincipalAsString(false);
return samlAuthenticationProvider;
}
#Bean
public SAMLContextProviderImpl contextProvider() {
return new SAMLContextProviderImpl();
}
#Bean
public static SAMLBootstrap sAMLBootstrap() {
return new SAMLBootstrap();
}
#Bean
public SAMLDefaultLogger samlLogger() {
return new SAMLDefaultLogger();
}
#Bean
public WebSSOProfileConsumer webSSOprofileConsumer() {
return new WebSSOProfileConsumerImpl();
}
#Bean
public WebSSOProfileConsumerHoKImpl hokWebSSOprofileConsumer() {
return new WebSSOProfileConsumerHoKImpl();
}
#Bean
public WebSSOProfile webSSOprofile() {
return new WebSSOProfileImpl();
}
#Bean
public WebSSOProfileConsumerHoKImpl hokWebSSOProfile() {
return new WebSSOProfileConsumerHoKImpl();
}
#Bean
public WebSSOProfileECPImpl ecpprofile() {
return new WebSSOProfileECPImpl();
}
#Bean
public SingleLogoutProfile logoutprofile() {
return new SingleLogoutProfileImpl();
}
#Bean
public KeyManager keyManager() {
DefaultResourceLoader loader = new DefaultResourceLoader();
Resource storeFile = loader
.getResource("classpath:/saml/keystore.jks");
String storePass = "password";
Map<String, String> passwords = new HashMap<String, String>();
passwords.put("mydomain", "password");
String defaultKey = "mydomain";
return new JKSKeyManager(storeFile, storePass, passwords, defaultKey);
}
#Bean
public TLSProtocolConfigurer tlsProtocolConfigurer() {
return new TLSProtocolConfigurer();
}
#Bean
public ProtocolSocketFactory socketFactory() {
return new TLSProtocolSocketFactory(keyManager(), null, "default");
}
#Bean
public Protocol socketFactoryProtocol() {
return new Protocol("https", socketFactory(), 443);
}
#Bean
public MethodInvokingFactoryBean socketFactoryInitialization() {
MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean();
methodInvokingFactoryBean.setTargetClass(Protocol.class);
methodInvokingFactoryBean.setTargetMethod("registerProtocol");
Object[] args = {"https", socketFactoryProtocol()};
methodInvokingFactoryBean.setArguments(args);
return methodInvokingFactoryBean;
}
#Bean
public WebSSOProfileOptions defaultWebSSOProfileOptions() {
WebSSOProfileOptions webSSOProfileOptions = new WebSSOProfileOptions();
webSSOProfileOptions.setIncludeScoping(false);
return webSSOProfileOptions;
}
// Entry point to initialize authentication, default values taken from
// properties file
#Bean
public SAMLEntryPoint samlEntryPoint() {
SAMLEntryPoint samlEntryPoint = new SAMLEntryPoint();
samlEntryPoint.setDefaultProfileOptions(defaultWebSSOProfileOptions());
return samlEntryPoint;
}
#Bean
public ExtendedMetadata extendedMetadata() {
ExtendedMetadata extendedMetadata = new ExtendedMetadata();
extendedMetadata.setIdpDiscoveryEnabled(true);
extendedMetadata.setSignMetadata(false);
return extendedMetadata;
}
#Bean
public SAMLDiscovery samlIDPDiscovery() {
SAMLDiscovery idpDiscovery = new SAMLDiscovery();
idpDiscovery.setIdpSelectionPath("/saml/idpSelection");
return idpDiscovery;
}
#Bean
#Qualifier("idp-ssocircle")
public ExtendedMetadataDelegate ssoCircleExtendedMetadataProvider()
throws MetadataProviderException {
String idpSSOCircleMetadataURL = "http://openam.com:8080/OpenAM/saml2/jsp/exportmetadata.jsp";
Timer backgroundTaskTimer = new Timer(true);
HTTPMetadataProvider httpMetadataProvider = new HTTPMetadataProvider(
backgroundTaskTimer, httpClient(), idpSSOCircleMetadataURL);
httpMetadataProvider.setParserPool(parserPool());
ExtendedMetadataDelegate extendedMetadataDelegate =
new ExtendedMetadataDelegate(httpMetadataProvider, extendedMetadata());
extendedMetadataDelegate.setMetadataTrustCheck(true);
extendedMetadataDelegate.setMetadataRequireSignature(false);
return extendedMetadataDelegate;
}
#Bean
#Qualifier("metadata")
public CachingMetadataManager metadata() throws MetadataProviderException {
List<MetadataProvider> providers = new ArrayList<MetadataProvider>();
providers.add(ssoCircleExtendedMetadataProvider());
return new CachingMetadataManager(providers);
}
// Filter automatically generates default SP metadata
#Bean
public MetadataGenerator metadataGenerator() {
MetadataGenerator metadataGenerator = new MetadataGenerator();
metadataGenerator.setEntityId("http://localhost:8080/spring-boot-security-saml2-sample/saml/web/metadata");
metadataGenerator.setExtendedMetadata(extendedMetadata());
metadataGenerator.setIncludeDiscoveryExtension(false);
metadataGenerator.setKeyManager(keyManager());
return metadataGenerator;
}
// The filter is waiting for connections on URL suffixed with filterSuffix
// and presents SP metadata there
#Bean
public MetadataDisplayFilter metadataDisplayFilter() {
return new MetadataDisplayFilter();
}
// Handler deciding where to redirect user after successful login
#Bean
public SavedRequestAwareAuthenticationSuccessHandler successRedirectHandler() {
SavedRequestAwareAuthenticationSuccessHandler successRedirectHandler =
new SavedRequestAwareAuthenticationSuccessHandler();
successRedirectHandler.setDefaultTargetUrl("/");
return successRedirectHandler;
}
// Handler deciding where to redirect user after failed login
#Bean
public SimpleUrlAuthenticationFailureHandler authenticationFailureHandler() {
SimpleUrlAuthenticationFailureHandler failureHandler =
new SimpleUrlAuthenticationFailureHandler();
failureHandler.setUseForward(true);
failureHandler.setDefaultFailureUrl("/error");
return failureHandler;
}
#Bean
public SAMLWebSSOHoKProcessingFilter samlWebSSOHoKProcessingFilter() throws Exception {
SAMLWebSSOHoKProcessingFilter samlWebSSOHoKProcessingFilter = new SAMLWebSSOHoKProcessingFilter();
samlWebSSOHoKProcessingFilter.setAuthenticationSuccessHandler(successRedirectHandler());
samlWebSSOHoKProcessingFilter.setAuthenticationManager(authenticationManager());
samlWebSSOHoKProcessingFilter.setAuthenticationFailureHandler(authenticationFailureHandler());
return samlWebSSOHoKProcessingFilter;
}
#Bean
public SAMLProcessingFilter samlWebSSOProcessingFilter() throws Exception {
SAMLProcessingFilter samlWebSSOProcessingFilter = new SAMLProcessingFilter();
samlWebSSOProcessingFilter.setAuthenticationManager(authenticationManager());
samlWebSSOProcessingFilter.setAuthenticationSuccessHandler(successRedirectHandler());
samlWebSSOProcessingFilter.setAuthenticationFailureHandler(authenticationFailureHandler());
return samlWebSSOProcessingFilter;
}
#Bean
public MetadataGeneratorFilter metadataGeneratorFilter() {
return new MetadataGeneratorFilter(metadataGenerator());
}
#Bean
public SimpleUrlLogoutSuccessHandler successLogoutHandler() {
SimpleUrlLogoutSuccessHandler successLogoutHandler = new SimpleUrlLogoutSuccessHandler();
successLogoutHandler.setDefaultTargetUrl("/");
return successLogoutHandler;
}
#Bean
public SecurityContextLogoutHandler logoutHandler() {
SecurityContextLogoutHandler logoutHandler =
new SecurityContextLogoutHandler();
logoutHandler.setInvalidateHttpSession(true);
logoutHandler.setClearAuthentication(true);
return logoutHandler;
}
#Bean
public SAMLLogoutProcessingFilter samlLogoutProcessingFilter() {
return new SAMLLogoutProcessingFilter(successLogoutHandler(),
logoutHandler());
}
#Bean
public SAMLLogoutFilter samlLogoutFilter() {
return new SAMLLogoutFilter(successLogoutHandler(),
new LogoutHandler[] { logoutHandler() },
new LogoutHandler[] { logoutHandler() });
}
private ArtifactResolutionProfile artifactResolutionProfile() {
final ArtifactResolutionProfileImpl artifactResolutionProfile =
new ArtifactResolutionProfileImpl(httpClient());
artifactResolutionProfile.setProcessor(new SAMLProcessorImpl(soapBinding()));
return artifactResolutionProfile;
}
#Bean
public HTTPArtifactBinding artifactBinding(ParserPool parserPool, VelocityEngine velocityEngine) {
return new HTTPArtifactBinding(parserPool, velocityEngine, artifactResolutionProfile());
}
#Bean
public HTTPSOAP11Binding soapBinding() {
return new HTTPSOAP11Binding(parserPool());
}
#Bean
public HTTPPostBinding httpPostBinding() {
return new HTTPPostBinding(parserPool(), velocityEngine());
}
#Bean
public HTTPRedirectDeflateBinding httpRedirectDeflateBinding() {
return new HTTPRedirectDeflateBinding(parserPool());
}
#Bean
public HTTPSOAP11Binding httpSOAP11Binding() {
return new HTTPSOAP11Binding(parserPool());
}
#Bean
public HTTPPAOS11Binding httpPAOS11Binding() {
return new HTTPPAOS11Binding(parserPool());
}
// Processor
#Bean
public SAMLProcessorImpl processor() {
Collection<SAMLBinding> bindings = new ArrayList<SAMLBinding>();
bindings.add(httpRedirectDeflateBinding());
bindings.add(httpPostBinding());
bindings.add(artifactBinding(parserPool(), velocityEngine()));
bindings.add(httpSOAP11Binding());
bindings.add(httpPAOS11Binding());
return new SAMLProcessorImpl(bindings);
}
#Bean
public FilterChainProxy samlFilter() throws Exception {
List<SecurityFilterChain> chains = new ArrayList<SecurityFilterChain>();
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/login/**"),
samlEntryPoint()));
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/logout/**"),
samlLogoutFilter()));
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/metadata/**"),
metadataDisplayFilter()));
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SSO/**"),
samlWebSSOProcessingFilter()));
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SSOHoK/**"),
samlWebSSOHoKProcessingFilter()));
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/SingleLogout/**"),
samlLogoutProcessingFilter()));
chains.add(new DefaultSecurityFilterChain(new AntPathRequestMatcher("/saml/discovery/**"),
samlIDPDiscovery()));
return new FilterChainProxy(chains);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic()
.authenticationEntryPoint(samlEntryPoint());
http
.csrf()
.disable();
http
.addFilterBefore(metadataGeneratorFilter(), ChannelProcessingFilter.class)
.addFilterAfter(samlFilter(), BasicAuthenticationFilter.class)
.addFilterBefore(sessionRepositoryFilter(sessionRepository(), httpSessionStrategy()),
ChannelProcessingFilter.class)
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
http
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/publicUrl").permitAll()
.antMatchers("/app/**").permitAll()
.antMatchers("/error").permitAll()
.antMatchers("/saml/**").permitAll()
.antMatchers("/landing").authenticated();
http
.logout()
.logoutSuccessUrl("/");
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(samlAuthenticationProvider());
}
#Bean
public HttpSessionStrategy httpSessionStrategy()
{
return new HeaderHttpSessionStrategy();
}
#Bean
public SessionRepositoryFilter<ExpiringSession> sessionRepositoryFilter(
SessionRepository<ExpiringSession> sessionRepository, HttpSessionStrategy httpSessionStrategy)
{
SessionRepositoryFilter<ExpiringSession> sessionRepositoryFilter = new SessionRepositoryFilter<>(
sessionRepository);
sessionRepositoryFilter.setHttpSessionStrategy(httpSessionStrategy);
return sessionRepositoryFilter;
}
#Bean
public SessionRepository<ExpiringSession> sessionRepository()
{
return new JPASessionRepository(1800);
}
}
JPA Session repository which implements SessionRepository is :
package com.security.repositories.session;
public class JPASessionRepository implements SessionRepository<ExpiringSession>
{
private static final Logger LOG = LogManager.getLogger(JPASessionRepository.class);
private int maxInactiveInterval = -1;
#Autowired
private SpringSessionRepository springSessionRepository;
public JPASessionRepository()
{
}
public JPASessionRepository(int maxInactiveInterval)
{
this.maxInactiveInterval = maxInactiveInterval;
}
#Override
public ExpiringSession createSession()
{
ExpiringSession result = new MapSession();
result.setMaxInactiveIntervalInSeconds(maxInactiveInterval);
return result;
}
#Transactional
#Override
public void save(ExpiringSession session)
{
springSessionRepository.save(convertToDomain(session));
}
#Transactional
#Override
public ExpiringSession getSession(String id)
{
SessionEntity sessionEntity = springSessionRepository.findOne(id);
ExpiringSession saved = null;
if(sessionEntity != null)
{
saved = convertToSession(sessionEntity);
}
if (saved == null)
{
return null;
}
if (saved.isExpired())
{
delete(saved.getId());
return null;
}
return saved;
}
#Override
public void delete(String id)
{
SessionEntity currentSession = springSessionRepository.findOne(id);
if (null != currentSession)
{
springSessionRepository.delete(id);
}
}
private SessionEntity convertToDomain(ExpiringSession session)
{
SessionEntity sessionEntity = new SessionEntity();
sessionEntity.setId(session.getId());
sessionEntity.setLastAccessedTime(session.getLastAccessedTime());
sessionEntity.setCreationTime(session.getCreationTime());
sessionEntity.setData(serializeAttributes(session));
return sessionEntity;
}
byte[] serializeAttributes(ExpiringSession session)
{
Map<String, Object> attributes = new HashMap<>();
for (String attrName : session.getAttributeNames())
{
attributes.put(attrName, session.getAttribute(attrName));
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer;
try
{
ObjectOutputStream objectOutputStream = new ObjectOutputStream(out);
objectOutputStream.writeObject(new SessionAttributes(attributes));
buffer = out.toByteArray();
objectOutputStream.close();
}
catch (IOException e)
{
throw new RuntimeException(e);
}
return buffer;
}
private ExpiringSession convertToSession(SessionEntity sessionEntity)
{
MapSession mapSession = new MapSession();
mapSession.setId(sessionEntity.getId());
mapSession.setLastAccessedTime(sessionEntity.getLastAccessedTime());
mapSession.setCreationTime(sessionEntity.getCreationTime());
mapSession.setMaxInactiveIntervalInSeconds(this.maxInactiveInterval);
SessionAttributes attributes = deserializeAttributes(sessionEntity);
if (attributes != null)
{
for (Map.Entry<String, Object> attribute : attributes.getAttributes().entrySet())
{
mapSession.setAttribute(attribute.getKey(), attribute.getValue());
}
}
return mapSession;
}
private SessionAttributes deserializeAttributes(SessionEntity sessionEntity)
{
SessionAttributes attributes = null;
if (sessionEntity.getData() != null && sessionEntity.getData().length > 0)
{
try
{
ObjectInputStream objectInputStream = new ObjectInputStream(
new ByteArrayInputStream(sessionEntity.getData()));
Object obj = objectInputStream.readObject();
attributes = (SessionAttributes) obj;
objectInputStream.close();
}
catch (IOException | ClassNotFoundException e)
{
LOG.warn(e);
//FIXME:How should this exception be handled?
}
}
return attributes;
}
public Integer getDefaultMaxInactiveInterval()
{
return maxInactiveInterval;
}
public void setDefaultMaxInactiveInterval(int maxInactiveInterval)
{
this.maxInactiveInterval = maxInactiveInterval;
}
public SpringSessionRepository getSpringSessionRepository()
{
return springSessionRepository;
}
public void setSpringSessionRepository(SpringSessionRepository springSessionRepository)
{
this.springSessionRepository = springSessionRepository;
}
}
SP Metadata
<?xml version="1.0" encoding="UTF-8"?><md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" ID="http___localhost_8080_spring-boot-security-saml2-sample_saml_web_metadata" entityID="http://localhost:8080/spring-boot-security-saml2-sample/saml/web/metadata"><md:SPSSODescriptor AuthnRequestsSigned="true" WantAssertionsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"><md:KeyDescriptor use="signing"><ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:X509Data><ds:X509Certificate>x509 certificate data</ds:X509Certificate></ds:X509Data></ds:KeyInfo></md:KeyDescriptor><md:KeyDescriptor use="encryption"><ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:X509Data><ds:X509Certificate>--x509 certificate</ds:X509Certificate></ds:X509Data></ds:KeyInfo></md:KeyDescriptor><md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://localhost:8080/saml/SingleLogout"/><md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://localhost:8080/saml/SingleLogout"/><md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat><md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat><md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</md:NameIDFormat><md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat><md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName</md:NameIDFormat><md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://localhost:8080/saml/SSO" index="0" isDefault="true"/><md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" Location="http://localhost:8080/saml/SSO" index="1"/></md:SPSSODescriptor></md:EntityDescriptor>
IDP metadata:-
<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" entityID="http://localhost:8081/OpenAM">
<IDPSSODescriptor WantAuthnRequestsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<KeyDescriptor use="signing">
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509Data>
<ds:X509Certificate>
sample certificate
</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</KeyDescriptor>
<ArtifactResolutionService index="0" isDefault="true" Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="http://localhost:8081/OpenAM/ArtifactResolver/metaAlias/idp"/>
<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://localhost:8081/OpenAM/IDPSloRedirect/metaAlias/idp" ResponseLocation="http://localhost:8081/OpenAM/IDPSloRedirect/metaAlias/idp"/>
<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://localhost:8081/OpenAM/IDPSloPOST/metaAlias/idp" ResponseLocation="http://localhost:8081/OpenAM/IDPSloPOST/metaAlias/idp"/>
<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="http://localhost:8081/OpenAM/IDPSloSoap/metaAlias/idp"/>
<ManageNameIDService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://localhost:8081/OpenAM/IDPMniRedirect/metaAlias/idp" ResponseLocation="http://localhost:8081/OpenAM/IDPMniRedirect/metaAlias/idp"/>
<ManageNameIDService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://localhost:8081/OpenAM/IDPMniPOST/metaAlias/idp" ResponseLocation="http://localhost:8081/OpenAM/IDPMniPOST/metaAlias/idp"/>
<ManageNameIDService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="http://localhost:8081/OpenAM/IDPMniSoap/metaAlias/idp"/>
<NameIDFormat>
urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
</NameIDFormat>
<NameIDFormat>
urn:oasis:names:tc:SAML:2.0:nameid-format:transient
</NameIDFormat>
<NameIDFormat>
urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
</NameIDFormat>
<NameIDFormat>
urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
</NameIDFormat>
<NameIDFormat>
urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName
</NameIDFormat>
<NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos</NameIDFormat>
<NameIDFormat>
urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName
</NameIDFormat>
<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://localhost:8081/OpenAM/SSORedirect/metaAlias/idp"/>
<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://localhost:8081/OpenAM/SSOPOST/metaAlias/idp"/>
<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="http://localhost:8081/OpenAM/SSOSoap/metaAlias/idp"/>
<NameIDMappingService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="http://localhost:8081/OpenAM/NIMSoap/metaAlias/idp"/>
<AssertionIDRequestService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="http://localhost:8081/OpenAM/AIDReqSoap/IDPRole/metaAlias/idp"/>
<AssertionIDRequestService Binding="urn:oasis:names:tc:SAML:2.0:bindings:URI" Location="http://localhost:8081/OpenAM/AIDReqUri/IDPRole/metaAlias/idp"/>
</IDPSSODescriptor>
</EntityDescriptor>
Please help me resolve the issue
This error is usually caused by a mismatch of the EntityIDs in some way. It looks like your metadata is configured correctly, so I did some looking and found this.
Make sure that application uses the same HttpSession during sending of the request and reception of the response. Typically, this problem arises when the auhentication request is initialized from localhost address or http scheme, while response is received at a public host name or https scheme. E.g., when initializing authentication from URL https://host:port/app/saml/login, the response must be received at https://host;port/app/saml/SSO, not http://host:port/app/saml/SSO or https://localhost:port/app/saml/SSO.
The checking of the InResponseToField can be disabled by re-configuring the context provider as follows:
<bean id="contextProvider" class="org.springframework.security.saml.context.SAMLContextProviderImpl">
<property name="storageFactory">
<bean class="org.springframework.security.saml.storage.EmptyStorageFactory"/>
</property>
</bean>
You should note that this should only be used for development purposes. You should probably use Spring Profiles to enable this configuration locally only because it's less secure.