Get Currently HTTP basic authenticated User in Spring [duplicate] - spring

This question already has answers here:
How to find out the currently logged-in user in Spring Boot?
(9 answers)
Closed 5 years ago.
I am using Http Basic Auth for a REST API where the username and password is sent in the header.This is the relevant config
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserService userService;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http
.antMatcher("/api/**")
.authorizeRequests()
.antMatchers("/api/**").authenticated()
.and()
.httpBasic();
}
A controller method
#RestController
public class ScheduleController {
#Autowired
ScheduleService scheduleService;
#RequestMapping(value ="api/schedule/{scheduleId}",method = RequestMethod.GET, produces = "application/json")
public ResponseEntity<Schedule> getOneSchedule(#PathVariable Long scheduleId) {
// Get the product given by Id
Schedule schedule = scheduleService.findOne(scheduleId);
if(schedule == null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
}
return ResponseEntity.status(HttpStatus.OK).body(schedule);
}
}
Is it possible to inside the "getOneSchedule" method to obtain the User object for the username/password provided?

Yes,
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
and to fetch the username, for example just use authentication.getName()

Related

Solution for dual security in spring boot application - OAuth2 (jwt token bearer) + X509 (certificates)

I tried to create a spring boot configuration with dual security checks on requests (Oauth2 token bearer and X509 certificates). I had 2 alternative ideas in mind, but cannot make it work either
dedicated endpoints for each type of security validation (/certif
for certification validation, /token for token validation)
all endpoints checked with either token or certificate validation
anything successfully would apply
This is my configuration that tries to achieve idea no 1:
#EnableResourceServer
#Configuration
#Order(1)
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Value("${xxx.auth.resourceId}")
private String resourceId;
#Autowired
private DefaultTokenServices tokenServices;
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId(resourceId)
.tokenServices(tokenServices)
.tokenExtractor(new BearerTokenExtractor());
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.requestMatchers()
.antMatchers("/unsecured/**")
.antMatchers("/token/**")
.and().authorizeRequests()
.antMatchers("/unsecured/**").permitAll()
.anyRequest().authenticated()
;
}
}
#EnableResourceServer
#Configuration
#Order(2)
public class X509ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.requestMatchers()
.antMatchers("/certif/**")
.and()
.authorizeRequests()
.antMatchers("/certif/**").hasAuthority("AUTH")
.and().x509().subjectPrincipalRegex("CN=(.*?)(?:,|$)").userDetailsService(userDetailsService());
}
#Bean
public UserDetailsService userDetailsService() {
return new UserDetailsService() {
#Override
public UserDetails loadUserByUsername(String username) {
if (username.startsWith("xxx") || username.startsWith("XXX")) {
return new User(username, "",
AuthorityUtils
.commaSeparatedStringToAuthorityList("AUTH"));
}
throw new UsernameNotFoundException("User not found!");
}
};
}
}
For some reason I cannot make it work because filter OAuth2AuthenticationProcessingFilter seems to be deleting the authorization token created by filter X509AuthenticationFilter when I make a call with a certificate to /certif/info. I must mention that ResourceServerConfiguration is working ok when used alone and the /token/info endpoint is called with a token.
Mentioned filters are in spring-security-oauth:2.3.8 & spring-security-web:5.6.2
Orders have been changed in every direction but they seem to have no effect on how the filters are applied.
Any idea what is going on and how can I avoid this problem in order to achieve the desired behaviour?
You can try to config just one Configuration class.
You can join the two methods named as "configure", in just one.
I didn't test this code, but tell me if you did it working.
#Override
public void configure(HttpSecurity http) throws Exception {
http.requestMatchers()
.antMatchers("/unsecured/**")
.antMatchers("/token/**")
.antMatchers("/certif/**")
.and().authorizeRequests()
.antMatchers("/unsecured/**").permitAll()
.anyRequest().authenticated()
.antMatchers("/certif/**").hasAuthority("AUTH")
.and().x509().subjectPrincipalRegex("CN=(.*?)(?:,|$)").userDetailsService(userDetailsService());
;
}

Spring Custom Security With MySQL And JPA Giving 403 Access Denied

I am trying to access my rest api on postman by providing authentication using UserDetailsService, but each time I am firing the request every time request giving 403 Access Denied. The behavior is same for POST and GET method. I have read the other issues logged on forum but every answers says it is due to CSRF, I disabled it but issue remains same.
Complete code is on : https://github.com/afulz29/spring-security-demo.git
Please help me, I am struggling with this issue since 3 days.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class ApplicationSecurityConfig extends WebSecurityConfigurerAdapter implements WebMvcConfigurer{
#Autowired
UserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http
.authorizeRequests()
.antMatchers("/api/**").authenticated().anyRequest().hasAnyRole("ADMIN");
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedMethods("*");
}
}
#RestController
#RequestMapping("/api")
public class UserController {
#Autowired
private UserService userService;
#GetMapping(path = "/users")
public User getUserById(#RequestParam("userId") Integer userId) {
return userService.getUserById(userId);
}
#PostMapping(path = "/users", consumes = MediaType.APPLICATION_JSON_VALUE)
public User addUser(#RequestBody User user) {
return userService.addUser(user);
}
}
I see couple of problems with your security config:
BASIC AUTH is not enabled but you are trying to do Basic Auth in postman
Do the following to enable Basic Auth
http
.authorizeRequests()
...
.and()
.httpBasic();
I guess the POST /api/users is a user registration endpoint. You must whitelist this endpoint so that anyone can register
http
.authorizeRequests()
.antMatchers( HttpMethod.POST,"/api/users").permitAll()
.antMatchers("/api/**").authenticated()
.anyRequest().hasAnyRole("ADMIN")
.and()
.httpBasic();
Test:
Create user
POST: localhost:8080/api/users
{
"userName" : "user1",
"password": "pass"
}
Get user info
GET: localhost:8080/api/users?userId=1 //use the correct ID
With Basic Auth: userName = user1, password = pass
BONUS Feedback:
User.userName --> you might want to make this field unique
#Repository this annotation is not required in your Repository interfaces
UserService interface. I don't see any reason to use the interface and impl.

Spring Boot Security UsernamePasswordAuthenticationFilter does not limit the login url to only POST

I looked all throughout and nobody else is having this issue that I can find. The authentication works correctly but the login url works on any HTTP method (GET, PUT, etc) vs. only working on POST. I tried manually setting filter.setPostOnly(true); on the custom JWTAuthenticationFilter I made, but it still allows on all methods.
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private static final String FILTER_PROCESS_URL = "/authentication";
private static final String HEALTH_RESOURCE_URL = "/health/**";
private CustomUserDetailService userDetailsService;
private BCryptPasswordEncoder bCryptPasswordEncoder;
public WebSecurityConfig(CowCalfUserDetailService userDetailsService, BCryptPasswordEncoder bCryptPasswordEncoder) {
this.userDetailsService = userDetailsService;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.cors().and().csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.POST, FILTER_PROCESS_URL).permitAll()
.antMatchers(HttpMethod.GET, HEALTH_RESOURCE_URL).permitAll()
.anyRequest().authenticated()
.and()
.addFilterBefore(getJWTAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilter(new JWTAuthorizationFilter(authenticationManagerBean()))
// this disables session creation on Spring Security
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(bCryptPasswordEncoder);
}
public JWTAuthenticationFilter getJWTAuthenticationFilter() throws Exception {
final JWTAuthenticationFilter filter = new JWTAuthenticationFilter(authenticationManagerBean());
filter.setPostOnly(true);
filter.setFilterProcessesUrl(FILTER_PROCESS_URL);
return filter;
}
}
If i understand you correctly, you want to limit the /authentication only to the POST Http-Method. If yes, you could achieve it by adding the following code snippet to your method in the "RestController" for the Authentication:
#RequestMapping(value = "/authentication", method = RequestMethod.POST)
RequestMapping Docs
I found out how setPostOnly() works. UsernamePasswordAuthenticationFilter.attemptAuthentication() checks for POST before attempting the authentication and throws an exception. This method I have overridden with my custom JWTAuthenticationFilter. I just did the same and added a check in my overridden method too. Thank you for the suggestions!

Getting credentials in a SpringBoot 2.1.4.RELEASE app

I have a SpringBoot 2.1.4.RELEASE app. with those methods in the security config file:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(publicMatchers()).permitAll()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login").defaultSuccessUrl("/calzadas/list")
.failureUrl("/login?error").permitAll()
.and()
.logout().permitAll();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.eraseCredentials(false)
.userDetailsService(userSecurityService)
.passwordEncoder(passwordEncoder());
}
and here my LoginController
#Controller
public class LoginController extends ICryptsController {
private static final Logger LOG = LoggerFactory.getLogger(LoginController.class);
/** The login view name */
public static final String LOGIN_VIEW_NAME = "login/login";
#RequestMapping(value={ "/", "/login"}, method = {RequestMethod.GET})
public String login() {
return serverContextPath + "/" + LOGIN_VIEW_NAME;
}
}
but I don't now from where to extract the credentials of the user in plain text (user / pwd)
You can retrieve the credentials from method arguments in your controller class. For example. Since you gave a success url, you can do this.
#Controller
public class LoginController extends ICryptsController {
...
#RequestMapping(value={ "/calzadas/list"}, method = {RequestMethod.GET})
public void list(Authentication auth) {
Object credentials = auth.getCredentials();
}
This is just one way, there are many examples on the internet.

Spring - Add a check for 3rd parameter during authentication

At a glance, I have API Back-end App written in Spring Boot which uses JWT for secured data transmission. I want to add 3rd parameter for authorization, so I should have login, password and storeID parameters. I am inspired by this answer How implement Spring security when login page having more field apart from user name and password? but when I followed proposed solution my 3rd parameter in not used. My impression is that I am missing something important in Security Config. Could you please point to my mistake?
SecurityConfig
#SuppressWarnings("SpringJavaAutowiringInspection")
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtAuthenticationEntryPoint unauthorizedHandler;
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private AuthenticationDetailsSource<HttpServletRequest, ?> webAuthenticationDetailsSourceImpl;
#Autowired
public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder
.authenticationProvider(myAuthProvider());
}
#Bean
public CustomUserDetailsAuthenticationProvider myAuthProvider() throws Exception {
CustomUserDetailsAuthenticationProvider provider = new CustomUserDetailsAuthenticationProvider();
provider.setPasswordEncoder(passwordEncoder());
provider.setUserDetailsService(userDetailsService);
return provider;
}
#Bean
public UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter() throws Exception {
UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter = new UsernamePasswordAuthenticationFilter();
usernamePasswordAuthenticationFilter.setAuthenticationManager(authenticationManager());
usernamePasswordAuthenticationFilter.setAuthenticationDetailsSource(webAuthenticationDetailsSourceImpl);
return usernamePasswordAuthenticationFilter;
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public JwtAuthenticationTokenFilter authenticationTokenFilterBean() throws Exception {
JwtAuthenticationTokenFilter authenticationTokenFilter = new JwtAuthenticationTokenFilter();
authenticationTokenFilter.setAuthenticationManager(authenticationManagerBean());
return authenticationTokenFilter;
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
// we don't need CSRF because our token is invulnerable
.csrf().disable()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
// don't create session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
// allow anonymous resource requests
.antMatchers(
HttpMethod.GET,
"/",
"/*.html",
"/favicon.ico",
"/**/*.html",
"/**/*.css",
"/**/*.js"
).permitAll()
.antMatchers("/auth/**").permitAll()
.anyRequest().authenticated();
// Custom JWT based security filter
httpSecurity
.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
// disable page caching
httpSecurity.headers().cacheControl();
}
}
I was under impression I can check against storeID field in WebAuthenticationDetailsSourceImpl, but looks like it has never been executed because I don't see anything related in log.
WebAuthenticationDetailsSourceImpl:
#Component
public class WebAuthenticationDetailsSourceImpl implements AuthenticationDetailsSource<HttpServletRequest, JwtAuthenticationRequest> {
#Override
public JwtAuthenticationRequest buildDetails(HttpServletRequest context) {
System.out.println("___#####_____");
System.out.println(context);
System.out.println("___#####_____");
return new JwtAuthenticationRequest();
}
}
cuz you don't insert "your" usernamePasswordAuthenticationFilter that set webAuthenticationDetailsSourceImpl to Spring Security's authentication filter chain.
perhaps current your authentication filter chain is
~
JwtAuthenticationTokenFilter
(Spring Security's original)UsernamePasswordAuthenticationFilter
~
hence,if you want to retrieve your additional parameter in "your" usernamePasswordAuthenticationFilter add this filter too like a JwtAuthenticationTokenFilter
but , if you want to simply retrieve parameter at JwtAuthenticationTokenFilter
use setAuthenticationDetailsSource at there
#Bean
public JwtAuthenticationTokenFilter authenticationTokenFilterBean() throws Exception {
JwtAuthenticationTokenFilter authenticationTokenFilter = new JwtAuthenticationTokenFilter();
authenticationTokenFilter.setAuthenticationManager(authenticationManagerBean());
authenticationTokenFilter.setAuthenticationDetailsSource(webAuthenticationDetailsSourceImpl);
return authenticationTokenFilter;
}

Resources