Get connected user information - spring-security

in a spring boot application, i use spring security and rest.
My setup of my security.
public class GymApplicationSecurity extends WebSecurityConfigurerAdapter {
#Autowired
private RESTAuthenticationEntryPoint authenticationEntryPoint;
#Autowired
private RESTAuthenticationFailureHandler authenticationFailureHandler;
#Autowired
private RESTAuthenticationSuccessHandler authenticationSuccessHandler;
#Autowired
private UserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/rest/**").authenticated();
http.csrf().disable();
http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);
http.formLogin().successHandler(authenticationSuccessHandler);
http.formLogin().failureHandler(authenticationFailureHandler);
http.logout().logoutUrl("/logout");
http.logout().logoutSuccessUrl("/");
}
}
When user do a requesty, in the controller, i would like to have it's id
I see on old tutorial
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
is it still ok with spring boot?
so i need to this in each methof of my controller

Related

Is it enough to remove access token and refresh token from front end application or somehow need to remove also from authorization server?

I am trying to learn oauth2 implementation. I am confused here, will it be enough to delete access token and refresh token from front end application or I have to delete it or invalidate any session or something from authorization server also. I am using spring-cloud-security for oauth2. My authorization server code as like below:
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private MyClientDetailsService clientDetailsService;
#Autowired
private MyUserDetailsService userDetailsService;
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.checkTokenAccess("isAuthenticated()").tokenKeyAccess("permitAll()");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetailsService);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager).userDetailsService(userDetailsService);
}
}

Securing Spring Boot with vaadin

Instead of using InMemory Auth I am trying to authenticate with BD user, so a created class user and roles, and added this code to this class
#EnableWebSecurity
#Configuration
public class SecurityConfig extends VaadinWebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetails customUserDetails;
#Autowired
CustomAuthenticationProvider customAuthenticationProvider;
#Override
protected void configure(HttpSecurity http) throws Exception {
// Set default security policy that permits Vaadin internal requests and
// denies all other
super.configure(http);
setLoginView(http, LoginView.class, "/logout");
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//auth.inMemoryAuthentication().withUser("user").password("{noop}userpass").roles("USER");
auth.userDetailsService(customUserDetails).passwordEncoder(passwordEncoder());
}
#Bean
public DaoAuthenticationProvider createDaoAuthenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(customUserDetails);
provider.setPasswordEncoder(passwordEncoder());
return provider;
}
}
But when i try to authenticate it does not accept
There is more than one way to do this, but usually it goes roughly like this. The relevant pieces, the complete solution is too lengthy for SO. There should not be anything Vaadin specific, but just follow Spring documentation on the matter.
#EnableWebSecurity
#Configuration
public class SecurityConfig extends VaadinWebSecurityConfigurerAdapter {
...
private final UserDetailsService userDetailsService;
#Autowired
public SecurityConfig (UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
super.configure(auth);
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
}
...
}
And then implement UserDetailsService according to Spring spec.
#Service
#Primary
public class UserDetailsServiceImpl implements UserDetailsService {
...
#Autowired
private UserRepository repository; // or what ever you have named it ...
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// implement this according to your actual implementation of user database
}
}

How to add an additional AuthenticationProvider without using WebSecurityConfigurerAdapter

Prior to Spring Security 5.7 it was possible to add additional AuthenticationProviders to the global AuthenticationManager this way:
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
...
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthenticationProvider);
}
}
With Spring Security 5.7 the WebSecurityConfigurerAdapter was deprecated.
Question: ho should i migrate this code to solve the deprecation?
When i try to register the additional AuthenticationProvider as #Bean, the autocreated authentication provider for username/password based authentication gets replaced, leading to
No AuthenticationProvider found for org.springframework.security.authentication.UsernamePasswordAuthenticationToken
I read the blog post https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter but found no hints about adding additional authentication providers to the global AuthenticationManager.
If you have a single AuthenticationProvider you can register it as a bean and it will be picked up by Spring Security:
#Bean
public CustomAuthenticationProvider customAuthenticationProvider() {
return new CustomAuthenticationProvider();
}
Alternatively, you can add additional AuthenticationProviders in the HttpSecurity configuration:
#Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
// ...
.authenticationProvider(new CustomAuthenticationProvider());
return http.build();
}
You can annotate your configuration class with #EnableGlobalAuthentication and will be able to configure a global instance of AuthenticationManagerBuilder:
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(customAuthenticationProvider);
}
Please see the related documentation: https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/config/annotation/authentication/configuration/EnableGlobalAuthentication.html
I had a same problem when I want to add an custom AuthenticationProvider using Spring Security without WebSecurityConfigurerAdapter.
Here is what I did.
Code with WebSecurityConfigurerAdapter
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
...
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthenticationProvider);
}
}
Code without WebSecurityConfigurerAdapter
#EnableWebSecurity
#EnableGlobalAuthentication
public class SecurityConfiguration {
...
#Autowired
CustomAuthenticationProvider customAuthenticationProvider;
#Autowired
void registerProvider(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(customAuthenticationProvider);
}
}
Note: #EnableGlobalAuthentication and registerProvider().
Hope this will help.
I had a similar problem. I have a custom user details service and I also use an additional custom authentication provider. One is for actual users and the custom provider is for automated devices.
This is my code with the WebSecurityConfigurerAdapter:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
...
#Autowired
private MyCustomAuthenticationProvider customAuthenticationProvider;
#Autowired
private UserDetailsService userDetailsService;
...
#Bean
PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
var encoder = passwordEncoder();
customAuthenticationProvider.encoder(encoder);
auth.userDetailsService(userDetailsService).passwordEncoder(encoder);
auth.authenticationProvider(customAuthenticationProvider);
}
...
}
This is my code without the WebSecurityConfigurerAdapter:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration {
...
#Autowired
private MyCustomAuthenticationProvider customAuthenticationProvider;
#Autowired
private UserDetailsService userDetailsService;
...
#Bean
PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
#Bean
AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
var encoder = passwordEncoder();
customAuthenticationProvider.encoder(encoder);
auth.userDetailsService(userDetailsService).passwordEncoder(encoder);
auth.authenticationProvider(customAuthenticationProvider);
}
...
}
note: you might need to set spring.main.allow-circular-references to true in your properties file for this to work.

authenticationFilter is not getting invoked in spring security

I having requirement of passing extra parameter along with username and password in spring security authentication process. After reading several threads I added custom authentication filter in spring security chain
below are my files
Filter class
public class AuthenticationFilter extends UsernamePasswordAuthenticationFilter {
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
String role = request.getParameter("role");
request.getSession().setAttribute("role", role);
return super.attemptAuthentication(request, response);
}
SecurityConfig class
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
RestAuthenticationSuccessHandler customizeAuthenticationSuccessHandler;
#Autowired
RestAuthenticationFailureHandler restAuthenticationFailureHandler;
#Autowired
UserDetailsService userDetailsService;
#Autowired
PasswordEncoder passwordEncoder;
#Autowired
private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
#Autowired
private AccessDeniedHandler restAccessDeniedHandler;
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.authorizeRequests().antMatchers("/api/common/**").permitAll()
.antMatchers("/api/student/**").access("hasRole('ROLE_STUDENT')")
.antMatchers("/api/staff/**").access("hasRole('ROLE_STAFF')").antMatchers("/sysAdmin/**").access("hasRole('ROLE_ADMIN')").and().formLogin()
.loginProcessingUrl("/api/common/login")
.successHandler(customizeAuthenticationSuccessHandler)
.failureHandler(restAuthenticationFailureHandler)
.usernameParameter("userName")
.passwordParameter("password")
.and().exceptionHandling().authenticationEntryPoint(restAuthenticationEntryPoint).accessDeniedHandler(restAccessDeniedHandler)
.and().csrf().disable();
}
#Bean
public UsernamePasswordAuthenticationFilter authenticationFilter() throws Exception {
AuthenticationFilter authFilter = new AuthenticationFilter();
authFilter.setUsernameParameter("username");
authFilter.setPasswordParameter("password");
authFilter.setAuthenticationManager(authenticationManagerBean());
return authFilter;
}
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider());
}
public AuthenticationProvider authProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
provider.setPasswordEncoder(passwordEncoder);
return provider;
}
}
Problem:
When I tried to authenticate user attemptAuthentication method of my custom filter class is not getting invoked and call directly go to UserDetails service class
Added FilterProcessingUrl to authenticateFilter object resolved the issue.
Updated authenticationFilter() method :
#Bean
public AuthenticationFilter authenticationFilter() throws Exception {
AuthenticationFilter authFilter = new AuthenticationFilter();
authFilter.setUsernameParameter("userName");
authFilter.setPasswordParameter("password");
authFilter.setFilterProcessesUrl("/api/common/login");
authFilter.setAuthenticationSuccessHandler(customizeAuthenticationSuccessHandler);
authFilter.setAuthenticationFailureHandler(restAuthenticationFailureHandler);
authFilter.setAuthenticationManager(authenticationManagerBean());
return authFilter;
}

Spring OAuth2, access ROLE with userInfoUri?

I'm in the process of building separate resource server and authhorization server. For now i'm using the user-info-uri in resource server to extract the Principal from the authorization server matching the access-token, with config:
spring:
oauth2:
resource:
userInfoUri: http://localhost:9999/uaa/user
In the resource server I have protected endpoints, based on role, as follows:
http
.authorizeRequests()
.antMatchers("/invoices/**").hasRole("END_USER")
.anyRequest().authenticated();
When I manually access the user-info-uri, I can see that the the authorities contain:
"authority": "ROLE_END_USER"
But when I try to access the /invoices resource I get an Access-Denied exception, and in the log I see:
OAuth2Authentication#bc5074a8: Principal: my-login; Credentials: [PROTECTED]; Authenticated: true; Details: remoteAddress=0:0:0:0:0:0:0:1, tokenType=BearertokenValue=<TOKEN>; Granted Authorities: ROLE_USER
Authoriteis = "ROLE_USER". Where does that come from, should'nt it be "ROLE_END_USER" at this point also?
I've seen implementations using a shared database for the storage of tokens, is that really necessary for what I want to achive?
Resource Server:
#SpringBootApplication
#EnableOAuth2Resource
public class EndUserResourceServiceApplication extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/invoices/**").hasRole("END_USER")
.anyRequest().authenticated();
}
public static void main(String[] args) {
SpringApplication.run(EndUserResourceServiceApplication.class, args);
}
}
Auth Server:
#SpringBootApplication
#RestController
#EnableResourceServer
public class ApiAuthServerApplication extends ResourceServerConfigurerAdapter {
#Configuration
#EnableWebSecurity
#Order(-10)
protected static class LoginConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetailsService userDetailsService;
#Bean(name = "authenticationManagerBean")
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
#Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin().permitAll()
.and()
.requestMatchers().antMatchers("/login", "/oauth/authorize", "/oauth/confirm_access")
.and()
.authorizeRequests().anyRequest().authenticated();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
#Configuration
#EnableAuthorizationServer
protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Autowired
private TokenStore tokenStore;
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Autowired
private CustomUserDetailsService userDetailsService;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.authenticationManager(this.authenticationManager)
.userDetailsService(this.userDetailsService)
.tokenStore(this.tokenStore);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("my-client")
.secret("our_s3cret")
.authorities("ROLE_CLIENT")
.authorizedGrantTypes("implicit", "password", "refresh_token")
.redirectUris("http://anywhere")
.scopes("read")
.autoApprove(true);
}
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
}
public static void main(String[] args) {
SpringApplication.run(ApiAuthServerApplication.class, args);
}
#RequestMapping("/user")
public Principal user(Principal user) {
return user;
}
}
Summary:
Can I use user-info-uri for validating access-tokens and to use "hasRole"
Is it necessary to use shared database for token storage when using separate resource server and authorization server?
I ended up using a shared token store, which works well at the moment

Resources