I want to make an authentication with password encoding.
When I use PasswordEncoder() and setPasswordEncryptor(), it gives me errors:
PasswordEncoder() : Cannot instantiate the type PasswordEncoder and for setPasswordEncryptor() : The method setPasswordEncryptor(StrongPasswordEncryptor) is undefined for the type PasswordEncoder.
I don't know what I should I do and I search for it too much.
thank you guys!
import org.jasypt.util.password.StrongPasswordEncryptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private AuthenticationProvider authenticationProvider;
#Autowired
#Qualifier("daoAuthenticationProvider")
public void setAuthenticationProvider(AuthenticationProvider authenticationProvider) {
this.authenticationProvider = authenticationProvider;
}
#Bean
public PasswordEncoder passwordEncoder(StrongPasswordEncryptor passwordEncryptor) {
PasswordEncoder passwordEncoder = new PasswordEncoder();
passwordEncoder.setPasswordEncryptor(passwordEncryptor);
return passwordEncoder;
}
#Bean
public DaoAuthenticationProvider daoAuthenticationProvider(PasswordEncoder passwordEncoder,
UserDetailsService userDetailsService) {
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder);
daoAuthenticationProvider.setUserDetailsService(userDetailsService);
return daoAuthenticationProvider;
}
#Autowired
public void configureAuthManager(AuthenticationManagerBuilder authenticationManagerBuilder) {
authenticationManagerBuilder.authenticationProvider(authenticationProvider);
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.authorizeRequests().antMatchers("/", "/products", "/product/show/*", "/console/*", "/h2-console/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login").permitAll()
.and()
.logout().permitAll();
httpSecurity.csrf().disable();
httpSecurity.headers().frameOptions().disable();
}
}
Try the following code snippet. It should work.
#Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder(11);
}
#Bean
public DaoAuthenticationProvider authProvider(UserDetailsService userDetailsService) {
final DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(encoder());
return authProvider;
}
Use this : Spring Boot 2
#Bean(name = "passwordEncoder")
#Qualifier("passwordEncoder")
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
Related
I am very new to Spring Security but I have 2 endpoints: /login and /welcome
When I call the endpoint /login, it tells me that I am logged in. My goal is that I stay logged in. Since for now, I get a 401 status when I call /welcome after I logged in.
I know that I could use setAuthentication() in the welcome() method but I don't want to do this for each controller. So how can I set the authentication only once after the login?
package com.example.spring_security.controller;
import com.example.spring_security.config.CustomUserDetails;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.provisioning.UserDetailsManager;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
#RestController
#RequestMapping("/")
public class HelloController {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private UserDetailsManager userDetailsManager;
#GetMapping("/welcome")
public String welcome() {
return "Welcome! You are logged in.";
}
#GetMapping("/home")
public String home() {
return "Home - Please register!";
}
#PostMapping("/register")
public void add(#RequestBody com.example.spring_security.entity.User userDto) {
if (userDto.getUsername() == null || userDto.getUsername().isEmpty()) {
throw new IllegalArgumentException("Username may not be empty or null");
}
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
UserDetails user = new CustomUserDetails(
userDto.getUsername(),
new BCryptPasswordEncoder().encode(userDto.getPassword()),
authorities,
false
);
userDetailsManager.createUser(user);
}
#PostMapping("/login")
public ResponseEntity<Object> login(#RequestBody CustomUserDetails user) {
try {
UserDetails userDetails = userDetailsService.loadUserByUsername(user.getUsername());
if (userDetails == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid credentials");
}
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
if (!passwordEncoder.matches(user.getPassword(), userDetails.getPassword())) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid credentials");
}
return ResponseEntity.ok("Login successful");
}
catch (Exception e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("invalid credentials");
}
}
}
package com.example.spring_security.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.JdbcUserDetailsManager;
import org.springframework.security.provisioning.UserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy;
import javax.sql.DataSource;
#Configuration
public class SecurityConfig {
private final DataSource dataSource;
public SecurityConfig(DataSource dataSource) {
this.dataSource = dataSource;
}
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.csrf().disable();
http.sessionManagement().sessionAuthenticationStrategy(sessionAuthenticationStrategy());
http.authorizeHttpRequests(auth ->
auth
.requestMatchers("/home", "/register", "/login").permitAll()
.requestMatchers("/welcome").hasRole("ROLE_USER")
.anyRequest().authenticated()
);
http.httpBasic();
http.logout().permitAll();
return http.build();
}
#Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService());
provider.setPasswordEncoder(passwordEncoder());
return provider;
}
#Bean
public UserDetailsService userDetailsService() {
JdbcUserDetailsManager manager = new JdbcUserDetailsManager();
manager.setDataSource(dataSource);
return manager;
}
#Bean
public PasswordEncoder passwordEncoder() {
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
System.out.println(passwordEncoder.encode("password"));
return new BCryptPasswordEncoder();
}
#Bean
public UserDetailsManager userDetailsManager() {
JdbcUserDetailsManager userDetailsManager = new JdbcUserDetailsManager();
userDetailsManager.setDataSource(dataSource);
return userDetailsManager;
}
#Bean
public SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new SessionFixationProtectionStrategy();
}
}
I use overridden values for spring security username and password. Following properties are in my application.properties.
spring.security.user.name=myUser
spring.security.user.password=myPassword
spring.security.user.roles=admin
I would like to encrypt the password value as follows:
spring.security.user.name=myUser
spring.security.user.password={bcrypt}hashedpassword valuevalue
spring.security.user.roles=admin
I added PasswordEncoder in my SpringConfig:
#Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
In some example I noticed that there is for AuthenitcationManagerBuilder but I do not know what datasource should be used. What else do I need to use encrypted password for default user?
#Autowired
public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {
Adding my Spring Security config as a reference:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/api/v1/custom").hasRole("admin")
.anyRequest().authenticated()
.and()
.httpBasic();
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
Grabbing:
$2a$12$7Bg57uTtN7GWIdiqRW4h5e/aOUFagHwkEGv9byUr0bb/QbUU8S4rS
for input:
hello
... from (e.g. thx): https://bcrypt-generator.com/
With:
spring-starter (web, security),
application.properties:
spring.security.user.password={bcrypt}$2a$12$7Bg57uTtN7GWIdiqRW4h5e/aOUFagHwkEGv9byUr0bb/QbUU8S4rS
and any (rest)controller:
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
#SpringBootApplication
#RestController
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
// #Bean
// public PasswordEncoder encoder() {
// return new BCryptPasswordEncoder();
// }
#GetMapping("secured")
public String secured() {
return "Hello Admin";
}
}
and without additional config,
we can login with user=user and password=hello.
Adding:
#Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
breaks this behavior!! To "fix it", we can (application.properties):
spring.security.user.password=$2a$12$7Bg57uTtN7GWIdiqRW4h5e/aOUFagHwkEGv9byUr0bb/QbUU8S4rS
.. omit the "tag" {bcrypt}! ...
Responsible for "tags"/capable of several algorithms is the DelegatingPasswordEncoder, which is auto configured (via [AuthenticationConfiguration|HttpSecurityConfiguration].LazyPasswordEncoder ..if no other PasswordEncoder bean found) and uses
PasswordEncoderFactories behind the scenes, like (currently) this:
#SuppressWarnings("deprecation")
public static PasswordEncoder createDelegatingPasswordEncoder() {
String encodingId = "bcrypt";
Map<String, PasswordEncoder> encoders = new HashMap<>();
encoders.put(encodingId, new BCryptPasswordEncoder());
encoders.put("ldap", new org.springframework.security.crypto.password.LdapShaPasswordEncoder());
encoders.put("MD4", new org.springframework.security.crypto.password.Md4PasswordEncoder());
encoders.put("MD5", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("MD5"));
encoders.put("noop", org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance());
encoders.put("pbkdf2", Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_5());
encoders.put("pbkdf2#SpringSecurity_v5_8", Pbkdf2PasswordEncoder.defaultsForSpringSecurity_v5_8());
encoders.put("scrypt", SCryptPasswordEncoder.defaultsForSpringSecurity_v4_1());
encoders.put("scrypt#SpringSecurity_v5_8", SCryptPasswordEncoder.defaultsForSpringSecurity_v5_8());
encoders.put("SHA-1", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-1"));
encoders.put("SHA-256",
new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-256"));
encoders.put("sha256", new org.springframework.security.crypto.password.StandardPasswordEncoder());
encoders.put("argon2", Argon2PasswordEncoder.defaultsForSpringSecurity_v5_2());
encoders.put("argon2#SpringSecurity_v5_8", Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8());
return new DelegatingPasswordEncoder(encodingId, encoders);
}
EDIT:
No, also introducing:
#Configuration
class WebSecurityConfig {
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf()
.disable()
.authorizeHttpRequests().requestMatchers("/secured").hasRole("admin")
.anyRequest()
.authenticated()
.and()
.httpBasic();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
return http.build();
}
}
}
Plus:
spring.security.user.roles=admin
Doesn't change my observations:
When "custom password encoder"
and $2a$12$7Bg57uTtN7GWIdiqRW4h5e/aOUFagHwkEGv9byUr0bb/QbUU8S4rS
can login with user:hello (and view /secured response)
with default/-out password encoder
and {bcrypt}$2a$12$7Bg57uTtN7GWIdiqRW4h5e/aOUFagHwkEGv9byUr0bb/QbUU8S4rS
can login with user:hello (and view /secured response)
(it only switches from form to basic login:)
Test (good/app config):
package com.example.demo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
#WebMvcTest
class WithAppConfigTest {
#Autowired
MockMvc mockMvc;
#Test
void expectAccess() throws Exception {
mockMvc.perform(
get("/secured")
.with(httpBasic("user", "hello"))
).andExpectAll(
authenticated(),
status().isOk(),
content().string("Hello Admin"));
}
#Test
void expectUnauthorized() throws Exception {
mockMvc.perform(
get("/secured")
.with(httpBasic("user", "wrong"))
).andExpectAll(
unauthenticated(),
status().isUnauthorized(),
content().bytes(new byte[0]));
}
}
Test forbidden:
#WebMvcTest(properties = "spring.security.user.roles=foo,bar,baz")
class WithFakeConfigTest {
#Autowired
MockMvc mockMvc;
#Test
void expectForbidden() throws Exception {
mockMvc.perform(
get("/secured")
.with(httpBasic("user", "hello"))
).andExpectAll(
authenticated(), // !, but:
status().isForbidden(),
content().bytes(new byte[0]));
}
}
I have a project using spring boot, spring security with oauth2. When I use
SecurityContextHolder.getContext().getAuthentication().getPrincipal()
this method returns only username end in the examples that I see this method returns UserDetails implentation.
Follow the settings
OAuthSecurityConfig.java:
package br.com.altha.api.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import br.com.altha.api.security.CustomUserDetailsService;
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled=true)
#EnableAuthorizationServer
#EnableResourceServer
#Order(SecurityProperties.BASIC_AUTH_ORDER-2)
#Import(Encoders.class)
public class OAuthSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private PasswordEncoder userPasswordEncoder;
#Autowired
private CustomUserDetailsService userDetailsService;
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(userPasswordEncoder);
}
}
AuthorizationServerConfig.java:
package br.com.altha.api.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import br.com.altha.api.security.CustomUserDetailsService;
#Configuration
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
private static final String SECRET = "PASSWORD";
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private PasswordEncoder oauthClientPasswordEncoder;
#Autowired
private CustomUserDetailsService userDetailsService;
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()").passwordEncoder(oauthClientPasswordEncoder);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("altha-adms")
.secret(oauthClientPasswordEncoder.encode(SECRET))
.scopes("write", "read")
.authorizedGrantTypes("password", "refresh_token")
.accessTokenValiditySeconds(60/*1800*/)
.refreshTokenValiditySeconds(1800);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenStore(tokenStore())
.accessTokenConverter(accessTokenConverter())
.authenticationManager(authenticationManager)
.reuseRefreshTokens(false)
.userDetailsService(userDetailsService);
}
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(SECRET);
return converter;
}
#Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
}
ResourceServerConfig.java:
package br.com.altha.api.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler;
import org.springframework.security.oauth2.provider.expression.OAuth2MethodSecurityExpressionHandler;
import br.com.altha.api.handler.RestExceptionHandler;
#Configuration
#Import(Encoders.class)
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Bean
public RestExceptionHandler handlerError() {
return new RestExceptionHandler();
}
#Bean
public MethodSecurityExpressionHandler createExpressionHandler() {
return new OAuth2MethodSecurityExpressionHandler();
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/public/**").permitAll()
.antMatchers("/private/**").authenticated()
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.stateless(true);
}
}
I was able to solve this problem with this code:
I added a bean to UserAuthenticationConverter
#Bean
public UserAuthenticationConverter userAuthenticationConverter() {
DefaultUserAuthenticationConverter defaultUserAuthenticationConverter = new DefaultUserAuthenticationConverter();
defaultUserAuthenticationConverter.setUserDetailsService(userDetailsService);
return defaultUserAuthenticationConverter;
}
After this, I set this bean in the JwtAccessTokenConverter
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
final JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
jwtAccessTokenConverter.setSigningKey(SECRET);
((DefaultAccessTokenConverter) jwtAccessTokenConverter.getAccessTokenConverter())
.setUserTokenConverter(userAuthenticationConverter());
return jwtAccessTokenConverter;
}
The cause would vary based on authentication/authorization technique but in my case I have 2 filters authentication/authorization the issue was passing the username instead of the whole user object to the UsernamePasswordAuthenticationToken in the authorization filter :
return new UsernamePasswordAuthenticationToken(userDetails.getUsername(), .....);
The first constructor argument is being set to Principle object.
So fix was to pass the whole userDetails object
return new UsernamePasswordAuthenticationToken(userDetails, .....);
With Spring Boot 1.5.x, you can implement a PrincipalExtractor and override it's Object extractPrincipal(Map<String, Object> map). The following example component has a UserdetailsService autowired in to lookup the UserDetails object based on the username.
#Component
public class MyPrincipalExtractor implements PrincipalExtractor {
private UserDetailsService userDetailsService;
#Value("${security.oauth2.client.principal-attribute}")
private String principalAttribute;
#Autowired
public MyPrincipalExtractor(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
#Override
public Object extractPrincipal(Map<String, Object> map) {
if (!map.containsKey(principalAttribute)) {
return null;
}
final String username = (String) map.get(principalAttribute);
try {
return userDetailsService.loadUserByUsername(username);
} catch (UsernameNotFoundException e) {
// This may be the first time this user is accessing the system,
// maybe you want to extract some other attributes from the map
// and return a different type of user object that can be used to
// create a new user.
}
}
}
Now the SecurityContextHolder.getContext().getAuthentication().getPrincipal() will contain a UserDetails object.
For a more detailed tutorial see:
https://www.baeldung.com/spring-security-oauth-principal-authorities-extractor
https://docs.spring.io/spring-security-oauth2-boot/docs/current-SNAPSHOT/api/org/springframework/boot/autoconfigure/security/oauth2/resource/PrincipalExtractor.html
I am very new to java spring security, and was following the Spring.io tutorial guide.
As part of this, I edited the WebSecurityConfig class as required:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
#Bean
#Override
public UserDetailsService userDetailsService() {
UserDetails user =
User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
}
Within the userDetailService() method, it uses withDefaultPasswordEncoder() which is now deprecated as seen in the docs: withDefaultPasswordEncoder()
Unfortunately, I have not been able to find an alternative to this, to complete this tutorial without using the deprecated method.
Would somebody be able to provide an alternative for this if possible?
Thanks!
note: I have attached a couple of screen shots of my error, as well as my gradle file
EDIT: deleted old answer, misunderstood the question. Here's the new one:
User.withDefaultPasswordEncoder() can still be used for demos, you don't have to worry if that's what you're doing - even if it's deprecated - but in production, you shouldn't have a plain text password in your source code.
What you should be doing instead of using your current userDetailsService() method is the following:
private static final String ENCODED_PASSWORD = "$2a$10$AIUufK8g6EFhBcumRRV2L.AQNz3Bjp7oDQVFiO5JJMBFZQ6x2/R/2";
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.passwordEncoder(passwordEncoder())
.withUser("user").password(ENCODED_PASSWORD).roles("USER");
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
Where ENCODED_PASSWORD is secret123 encoded with BCrypt. You can also encode it programmatically like so: passwordEncoder().encode("secret123").
That way, even if you push your code to a public repository, people won't know the password because ENCODED_PASSWORD only shows the encoded (and hashed) version of the password and not the plain text version, but because you know that $2a$10$AIUufK8g6EFhBcumRRV2L.AQNz3Bjp7oDQVFiO5JJMBFZQ6x2/R/2 is actually the encoded password of the string secret123 whereas others don't, your in-memory user with the credentials user:secret123 won't be compromised.
Note that I'm using leaving it in a static variable for the sake of the example.
In spring security 5.7.3 WebSecurityConfigurerAdapter will be deprecated and so we have
to provide bean of UserDetailsService interface.
Sample code
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurity {
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.formLogin(form -> {
form.loginPage("/login")
.permitAll();
}).authorizeRequests();
return http.build();
}
#Bean
#Description("In memory Userdetails service registered since DB doesn't have user table ")
public UserDetailsService users() {
// The builder will ensure the passwords are encoded before saving in memory
UserDetails user = User.builder()
.username("user")
.password("password")
.roles("USER")
.build();
UserDetails admin = User.builder()
.username("admin")
.password("password")
.roles("USER", "ADMIN")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Using the passwordEncoder.encode() would be like this
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.passwordEncoder(passwordEncoder())
.withUser("user")
.password(passwordEncoder().encode("miClave"))
.roles("USER");
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
The withDefaultPasswordEncoder() method is now deprecated in favor of passwordEncoder() which accepts a PasswordEncoder implementation. It's recommended to use a stronger password encoding mechanism such as bcrypt or scrypt.
To use bcrypt, you can do the following:
package com.example.securingweb;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
#Configuration
#EnableWebSecurity
public class WebSecurityConfig {
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((requests) -> requests
.requestMatchers("/", "/home").permitAll()
.anyRequest().authenticated()
)
.formLogin((form) -> form
.loginPage("/login")
.permitAll()
)
.logout((logout) -> logout.permitAll());
return http.build();
}
#Bean
public UserDetailsService userDetailsService() {
UserDetails user =
User.builder()
.username("user")
.password(passwordEncoder().encode("password"))
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
The below code is the new implementation of WebSecurityConfig.java file https://spring.io/guides/gs/securing-web/
I am experimenting with Spring Boot Security OAuth2. I would like to make some paths publicly accessible; that is, not requiring authentication. However, I am receiving the following error:
{"error":"unauthorized","error_description":"Full authentication is required to access this resource"}
PublicController
#RequestMapping("/public")
public class PublicController {
#RequestMapping(method= RequestMethod.GET)
public String test() {
return "Hello World!";
}
}
Now, normally this would be a good thing (since security is the goal), but, I have explicitly told the application to permitAll() on that path.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.password.PasswordEncoder;
import com.midamcorp.resource_server.service.OAuthUserDetailsService;
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private OAuthUserDetailsService userService;
#Autowired
private PasswordEncoder passwordEncoder;
#Bean
#Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
// Hash password
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService)
.passwordEncoder(passwordEncoder);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().antMatchers("/public/**")
.permitAll()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.httpBasic()
.realmName("test")
.and()
.csrf()
.disable();
}
}
I have tried various combinations, including using antMatches(/**) and removing the portions after the and(). However, I still receive the same error.
Thanks.
EDIT:
ResourceServerConfig
#EnableResourceServer
#Configuration
public class ResourceServerConifig extends ResourceServerConfigurerAdapter {
#Autowired
private TokenStore tokenStore;
private final AppConfig appConfig;
private AuthenticationManager authenticationManager;
#Autowired
public ResourceServerConifig(AuthenticationManager authenticationManager, AppConfig appConfig) {
this.authenticationManager = authenticationManager;
this.appConfig = appConfig;
}
#Bean
#Primary //Making this primary to avoid any accidental duplication with another token service instance of the same name
ResourceServerTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore);
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.tokenServices(tokenServices());
}
}
ApplicationConfig
#Configuration
#EnableResourceServer
public class AppConfig {
#Bean
public TokenStore tokenStore() {
// return new JwtTokenStore(accessTokenConverter());
return new JwtTokenStore((jwtTokenEnhancer()));
}
#Bean
protected JwtAccessTokenConverter jwtTokenEnhancer() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
Resource resource = new ClassPathResource("public.cert");
String publicKey = null;
try {
publicKey = new String(FileCopyUtils.copyToByteArray(resource.getInputStream()));
} catch (IOException e) {
throw new RuntimeException(e);
}
converter.setVerifierKey(publicKey);
return converter;
}
}
In your application.yml you could add this:
security:
ignored: /public,/public/**