001/* 002 * Copyright 2012-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.boot.autoconfigure.security.reactive; 018 019import java.util.List; 020import java.util.regex.Pattern; 021 022import org.apache.commons.logging.Log; 023import org.apache.commons.logging.LogFactory; 024 025import org.springframework.beans.factory.ObjectProvider; 026import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 027import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 028import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; 029import org.springframework.boot.autoconfigure.security.SecurityProperties; 030import org.springframework.context.annotation.Bean; 031import org.springframework.context.annotation.Configuration; 032import org.springframework.security.authentication.ReactiveAuthenticationManager; 033import org.springframework.security.core.userdetails.MapReactiveUserDetailsService; 034import org.springframework.security.core.userdetails.ReactiveUserDetailsService; 035import org.springframework.security.core.userdetails.User; 036import org.springframework.security.core.userdetails.UserDetails; 037import org.springframework.security.crypto.password.PasswordEncoder; 038import org.springframework.util.StringUtils; 039 040/** 041 * Default user {@link Configuration} for a reactive web application. Configures a 042 * {@link ReactiveUserDetailsService} with a default user and generated password. This 043 * backs-off completely if there is a bean of type {@link ReactiveUserDetailsService} or 044 * {@link ReactiveAuthenticationManager}. 045 * 046 * @author Madhura Bhave 047 */ 048@Configuration 049@ConditionalOnClass({ ReactiveAuthenticationManager.class }) 050@ConditionalOnMissingBean({ ReactiveAuthenticationManager.class, 051 ReactiveUserDetailsService.class }) 052@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE) 053public class ReactiveUserDetailsServiceAutoConfiguration { 054 055 private static final String NOOP_PASSWORD_PREFIX = "{noop}"; 056 057 private static final Pattern PASSWORD_ALGORITHM_PATTERN = Pattern 058 .compile("^\\{.+}.*$"); 059 060 private static final Log logger = LogFactory 061 .getLog(ReactiveUserDetailsServiceAutoConfiguration.class); 062 063 @Bean 064 public MapReactiveUserDetailsService reactiveUserDetailsService( 065 SecurityProperties properties, 066 ObjectProvider<PasswordEncoder> passwordEncoder) { 067 SecurityProperties.User user = properties.getUser(); 068 UserDetails userDetails = getUserDetails(user, 069 getOrDeducePassword(user, passwordEncoder.getIfAvailable())); 070 return new MapReactiveUserDetailsService(userDetails); 071 } 072 073 private UserDetails getUserDetails(SecurityProperties.User user, String password) { 074 List<String> roles = user.getRoles(); 075 return User.withUsername(user.getName()).password(password) 076 .roles(StringUtils.toStringArray(roles)).build(); 077 } 078 079 private String getOrDeducePassword(SecurityProperties.User user, 080 PasswordEncoder encoder) { 081 String password = user.getPassword(); 082 if (user.isPasswordGenerated()) { 083 logger.info(String.format("%n%nUsing generated security password: %s%n", 084 user.getPassword())); 085 } 086 if (encoder != null || PASSWORD_ALGORITHM_PATTERN.matcher(password).matches()) { 087 return password; 088 } 089 return NOOP_PASSWORD_PREFIX + password; 090 } 091 092}