SecurityContextHolder returns correct user's value but Authentication object returns null - spring

I've used 2 login pages, 1 for users and another for admin. But I have stored admin information in memory but have fetched user's information from the database. The problem here is, when I want to use Authentication object it returns null. But SecurityContextHolder gives me the perfect value. I want to set this Authentication value globally, so that my every method can have it.
Here is my SecurityConfig class
// admin login class
#Configuration
#Order(1)
public class AdminAuthorization extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/admin/**").authorizeRequests().anyRequest().hasRole("ADMIN").and().formLogin()
.loginPage("/adminLogin").loginProcessingUrl("/admin/dashboard").and().csrf().disable();
}
// for authentication
#Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("admin").password(encoder().encode("admin")).roles("ADMIN");
}
}
// Publisher login class
#Configuration
#Order(2)
public class PublisherAuthorization extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) {
try {
http.authorizeRequests()
.antMatchers("/publisher/**").hasRole("PUBLISHER")
.and().formLogin().loginPage("/login")
.loginProcessingUrl("/login").successForwardUrl("/publisher/welcome")
.failureUrl("/login?error").usernameParameter("username").passwordParameter("password");
} catch (Exception e) {
e.printStackTrace();
}
}
// for authentication
#Autowired
public void configure(AuthenticationManagerBuilder auth) {
try {
auth.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery("select username, password, active" + " from publisher where username=?")
.passwordEncoder(encoder())
.authoritiesByUsernameQuery("select username, authority " + "from authorities where username=?");
} catch (Exception e) {
e.printStackTrace();
}
}
}
#Bean
public static PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
HomeController
#PostMapping(value = { "/welcome", "/welcome/{QuestionPageNumber}/{ArticlePageNumber}" })
public ModelAndView page(Authentication auth, #PathVariable Optional<Integer> QuestionPageNumber,
#PathVariable Optional<Integer> ArticlePageNumber) {
System.out.println(auth==null); //returns true
//but
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
System.out.println(authentication.getName()); //returns correct user's information
Now, the problem is, I don't to use this code
SecurityContextHolder.getContext().getAuthentication();
on every line.(I don't know the reason why!!)..
I'm unable to collect my publisher's information. Admin is working fine.

Try this code instead of your in memory auth implementation:
import org.springframework.security.core.userdetails.User;
String username = "ADMIN";
String encodedPassword = new BCryptPasswordEncoder().encode("admin");
List<SimpleGrantedAuthority> authList = Arrays.asList(new SimpleGrantedAuthority("ROLE_ADMIN"));
User user = new User(username, encodedPassword, authList);
auth.inMemoryAuthentication().withUser(user);

Related

spring boot security custom successHandler with rest not working

not sure if my question is good..
Perhaps I was looking very badly for information about the spring security
In general, I hope it will not be difficult for you to answer.
The question is, I use spring security with my login page. The login page is just in the public templates folder. I do not create a separate Controller for it that would return the view page (would it be correct to create a controller for it that would return the view login page?). In any case, my code works even without this page view controller. But only my custom SuccessHandler does not work (which, after login, checks by roles and would redirect to another page).
Should I redirect by role to the appropriate pages using a different approach? (I mean if ADMIN_ROLE after login is redirected to the admin-panel.html)
my security
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
public class CustomWebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private UserServiceImpl userServiceImpl;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and()
.authorizeRequests()
.antMatchers("/", "/templates/sign-up.html").permitAll()
.antMatchers("/api/users", "/api/users/login").permitAll()
.antMatchers("/templates/admin-panel.html").hasRole("ADMIN")
.antMatchers("/all-users").hasRole("ADMIN")
.antMatchers("/news").hasRole("USER")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/templates/login.html")
.defaultSuccessUrl("/")
.permitAll()
.successHandler(myAuthenticationSuccessHandler())
.and()
.logout()
.permitAll()
.logoutSuccessUrl("/index.html");
http.csrf().disable();
}
#Override
public void configure(WebSecurity web) {
web
.ignoring()
.antMatchers("/css/**")
.antMatchers("/js/**")
.antMatchers("/static/**")
.antMatchers("/resources/**");
}
#Autowired
protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userServiceImpl).passwordEncoder(bCryptPasswordEncoder());
}
#Bean
public AuthenticationSuccessHandler myAuthenticationSuccessHandler(){
return new CustomAuthenticationSuccessHandler();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
my custom success handler
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
protected final Log logger = LogFactory.getLog(this.getClass());
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
public CustomAuthenticationSuccessHandler() {
super();
}
// API
#Override
public void onAuthenticationSuccess(final HttpServletRequest request, final HttpServletResponse response, final Authentication authentication) throws IOException {
handle(request, response, authentication);
clearAuthenticationAttributes(request);
}
// IMPL
protected void handle(final HttpServletRequest request, final HttpServletResponse response, final Authentication authentication) throws IOException {
final String targetUrl = determineTargetUrl(authentication);
if (response.isCommitted()) {
logger.debug("Response has already been committed. Unable to redirect to " + targetUrl);
return;
}
redirectStrategy.sendRedirect(request, response, targetUrl);
}
protected String determineTargetUrl(final Authentication authentication) {
Map<String, String> roleTargetUrlMap = new HashMap<>();
roleTargetUrlMap.put("ROLE_USER", "/index.html");
roleTargetUrlMap.put("ROLE_ADMIN", "/templates/admin-panel.html");
final Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
for (final GrantedAuthority grantedAuthority : authorities) {
String authorityName = grantedAuthority.getAuthority();
if(roleTargetUrlMap.containsKey(authorityName)) {
return roleTargetUrlMap.get(authorityName);
}
}
throw new IllegalStateException();
}
/**
* Removes temporary authentication-related data which may have been stored in the session
* during the authentication process.
*/
protected final void clearAuthenticationAttributes(final HttpServletRequest request) {
final HttpSession session = request.getSession(false);
if (session == null) {
return;
}
session.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
}
}
my controller
#CrossOrigin
#RestController
#RequestMapping("/api/users")
public class UserController {
private final UserServiceImpl userService;
private AuthenticationManager authenticationManager;
public UserController(UserServiceImpl userService, AuthenticationManager authenticationManager) {
this.userService = userService;
this.authenticationManager = authenticationManager;
}
#PostMapping
public ResponseEntity<?> register(#RequestBody UserDTO user) {
try {
userService.register(user);
return new ResponseEntity<>("User added", HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>(e, HttpStatus.BAD_REQUEST);
}
}
#PostMapping(value = "/login")
public ResponseEntity<?> login(#RequestBody UserDTO user, HttpServletResponse response) {
try {
Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword()));
boolean isAuthenticated = isAuthenticated(authentication);
if (isAuthenticated) {
SecurityContextHolder.getContext().setAuthentication(authentication);
// response.sendRedirect("/templates/admin-panel.html");
// my pathetic attempt to create a redirect to another page
}
return new ResponseEntity<>("user authenticated", HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>(e, HttpStatus.FORBIDDEN);
}
}
private boolean isAuthenticated(Authentication authentication) {
return authentication != null && !(authentication instanceof AnonymousAuthenticationToken) && authentication.isAuthenticated();
}
my static files
enter image description here
My Guess, as you didn't post your login page itself:
You don't need a controller listening to POST /login this normally automatically registered by Spring Security with all security related authentication stuff. No need to try it by yourself as in UserController.login(). I guess by regsitering this endpoint you override / deactivate the regular spring security behaviour.
Normally you just need a login page with a form that posts correctly to /login. The handling on the backend side is done by spring security itself.
See https://spring.io/guides/gs/securing-web/ for a minimal worling setup.

Successful Spring OAuth2 login with empty authorities

I implemented the login of my Spring Boot web app using OAuth2 and everything works fine.
The only problem is that the logged in user does not has the authorities information saved inside the session so each time I request a url and the controller has the annotation #PreAuthorize("hasRole('USER')") I get rejected.
SecurityConfiguration class:
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
#EnableJpaRepositories(basePackageClasses = UserRepository.class)
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private CustomOAuth2UserService customOAuth2UserService;
#Autowired
private CustomUserDetailsService userDetailsService;
#Autowired
private OAuth2AuthenticationFailureHandler oAuth2AuthenticationFailureHandler;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
super.configure(auth);
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.formLogin()
.loginPage("/login")
.failureUrl("/login?error=true")
.and()
.logout()
.logoutSuccessUrl("/")
.deleteCookies("JSESSIONID")
.invalidateHttpSession(true)
.and()
.oauth2Login()
.loginPage("/login")
.failureUrl("/login?error=true")
.userInfoEndpoint()
.userService(customOAuth2UserService)
.and()
.failureHandler(oAuth2AuthenticationFailureHandler);
}
#Bean
public BCryptPasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
This is the CustomOAuth2UserService class:
#Service
public class CustomOAuth2UserService extends DefaultOAuth2UserService {
#Autowired
private UserService userService;
#Override
public OAuth2User loadUser(OAuth2UserRequest oAuth2UserRequest) throws OAuth2AuthenticationException {
OAuth2User oAuth2User = super.loadUser(oAuth2UserRequest);
try {
return processOAuth2User(oAuth2UserRequest, oAuth2User);
}catch (Exception ex) {
// Throwing an instance of AuthenticationException will trigger the OAuth2AuthenticationFailureHandler
throw new InternalAuthenticationServiceException(ex.getMessage(), ex.getCause());
}
}
private OAuth2User processOAuth2User(OAuth2UserRequest oAuth2UserRequest, OAuth2User oAuth2User) {
OAuth2UserInfo oAuth2UserInfo = OAuth2UserInfoFactory.getOAuth2UserInfo(oAuth2UserRequest.getClientRegistration().getRegistrationId(), oAuth2User.getAttributes());
if(StringUtils.isEmpty(oAuth2UserInfo.getEmail())) {
throw new RuntimeException("Id not found from OAuth2 provider");
}
User user;
try {
user = userService.getByEmail(oAuth2UserInfo.getEmail());
if(!user.getProvider().toString().equalsIgnoreCase(oAuth2UserRequest.getClientRegistration().getRegistrationId())) throw new EmailAlreadyTakenException("email-already-taken");
} catch (UserNotFoundException e) {
user = registerNewUser(oAuth2UserRequest, oAuth2UserInfo);
}
return new CustomUserDetails(user);
}
private User registerNewUser(OAuth2UserRequest oAuth2UserRequest, OAuth2UserInfo oAuth2UserInfo) {
User user = new User();
user.setProvider(AuthProvider.valueOf(oAuth2UserRequest.getClientRegistration().getRegistrationId()));
Identity identity = new Identity(user);
if(oAuth2UserInfo.getFirstName() != null && !oAuth2UserInfo.getFirstName().equalsIgnoreCase(""))
identity.setFirstName(oAuth2UserInfo.getFirstName());
if(oAuth2UserInfo.getLastName() != null && !oAuth2UserInfo.getLastName().equalsIgnoreCase(""))
identity.setSecondName(oAuth2UserInfo.getLastName());
user.setIdentity(identity);
user.setEmail(oAuth2UserInfo.getEmail());
user.setConfirmedRegistration(true);
boolean flag = false;
String username = oAuth2UserInfo.getName().toLowerCase().replaceAll("\\s+", "");
user.setUsername(username);
return userService.addFacebookUser(user);
}
}
This a part of the application.properties file:
spring.security.oauth2.client.registration.facebook.client-id=***
spring.security.oauth2.client.registration.facebook.client-secret=***
spring.security.oauth2.client.registration.facebook.scope=email,public_profile
spring.security.oauth2.client.registration.google.client-id=***
spring.security.oauth2.client.registration.google.client-secret=***
spring.security.oauth2.client.registration.google.scope=email,profile
spring.security.oauth2.client.provider.facebook.authorizationUri = https://www.facebook.com/v3.0/dialog/oauth
spring.security.oauth2.client.provider.facebook.tokenUri = https://graph.facebook.com/v3.0/oauth/access_token
spring.security.oauth2.client.provider.facebook.userInfoUri = https://graph.facebook.com/v3.0/me?fields=id,first_name,middle_name,last_name,name,email,verified,is_verified,picture
Once logged in the user can call this url /users/{username} but when he login with facebook or google through OAuth2, he gets rejected because the authorities list is empty. When he login with his webapp credential, the authorities list contains USER_ROLE and he is allowed to procede.
#PreAuthorize("hasRole('USER')")
#GetRequest("users/{username}")
public String getUser(#PathVariable String username, #PathVariable String subsection, Model model, Principal principal) throws IllegalAccessException, UserNotFoundException {
User user = userService.getByUsername(principal.getName());
model.addAttribute("user", user);
return "user";
}
Inside principal object there are:
When logged in with OAuth2:
principal: type CustomUserDetails (user information)
authorizedClientRegistrationId: type String ("google", "facebook")
authorities: type Collections$UnmodifiableRandomAccessList (empty)
details: null
authenticated: type boolean (true)
When logged in with local credentials:
principal: type CustomUserDetails (user information)
credentials: null
authorities: type Collections$UnmodifiableRandomAccessList
index:0 type SimpleGrantedAuthority ("USER_ROLE")
details: type WebAuthenticationDetails (remote address, sessionId)
authenticated: type boolean (true)
After some time of debugging I found the solution! I was not configuring correctly the roles of my user.
Inside the registerNewUser method of my custom OAuth2UserService I wasn't setting the Role of the User. I just added the line:
user.setRoles(new HashSet<>(Collections.singletonList(new Role("ROLE_USER"))));
and everything started to work! So now when the OAuth2User's authorities get asked, it just calls the getAuthorities of CustomUserDetails (my implementation of OAuth2User) and it calls the getRoles method of the User.
CustomUserDetails class:
public class CustomUserDetails extends User implements UserDetails, OAuth2User {
public CustomUserDetails() {
}
public CustomUserDetails(String username, String email, String password, Set<Role> roles) {
super(username, email, password, roles);
}
public CustomUserDetails(User user) {
super(user.getUsername(), user.getEmail(), user.getPassword(), user.getRoles());
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return getRoles()
.stream()
.map(role -> new SimpleGrantedAuthority(role.getRole()))
.collect(Collectors.toList());
}
#Override
public Map<String, Object> getAttributes() {
return null;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
#Override
public String getName() {
return null;
}
}

Spring Security REST Login

I got question about login with REST API with Spring Security. As far as login with default login window provided by Spring Security is working and it is authenticating with Database, I have no idea how to make my own login. I know how to substitute the form for my own, but where should I send the data? Should I POST it for some address? I made basic form with username and password.
Try this one, it might help you... at least to understand what you are missing.
This code is not guarantee to be worked 100%, some part is intentionally missed (error handling and it's format, loading user, some checks, Session API).
The basic idea is you must to register a filter (react on all secured request for authentication process) and a provider that later on will be able to load authonticated user and create for you security context (e.g. you know each request is handled per thread and this user can be obtained by SecurityContextHolder/ThreadLocal).
And you need to create a separate controller to handle the initial case for creating a user session aka login/Authorization. Response of this API must to contain some session's GUID to use it as value of header later on: Authentication: Bearer <value>
some spec: https://www.rfc-editor.org/rfc/rfc6750
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)//optional
#Import(RestSecurityConfig.TokenAuthenticationProvider.class)// one of the way to create spring bean
public class RestSecurityConfig extends WebSecurityConfigurerAdapter {
private static final RequestMatcher PUBLIC_URLS = new OrRequestMatcher(
new AntPathRequestMatcher("/actuator/*"),
new AntPathRequestMatcher("/some_api_to_login", POST), // this must be public
);
private static final RequestMatcher PROTECTED_URLS = new NegatedRequestMatcher(PUBLIC_URLS);
// better to move it out as a separate class
public static class TokenAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
#Override
public boolean supports(Class<?> authentication) {
return MyAuthenticationToken.class.isAssignableFrom(authentication);
}
#Override
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
}
#Override
protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
return null; // service/dao.loadUser
}
}
public static class TokenAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public TokenAuthenticationFilter(RequestMatcher requiresAuthenticationRequestMatcher) {
super(requiresAuthenticationRequestMatcher);
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
Authentication auth = new MyAuthenticationToken(request.getHeader("Authentication"));
return getAuthenticationManager().authenticate(auth);
}
}
#Autowired
TokenAuthenticationProvider authenticationProvider;
#Override
protected void configure(final AuthenticationManagerBuilder auth) {
auth.authenticationProvider(authenticationProvider);
}
#Override
public void configure(final WebSecurity web) {
web.ignoring().requestMatchers(PUBLIC_URLS);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// maybe some of the tuning you might not need
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.exceptionHandling()
.defaultAuthenticationEntryPointFor(new Http403ForbiddenEntryPoint(), PROTECTED_URLS).and()
.authorizeRequests().anyRequest().authenticated().and()
.cors().and()
.anonymous().disable()
.rememberMe().disable()
.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.logout().disable();
// it's important
http.addFilterBefore(tokenAuthenticationFilter(), AnonymousAuthenticationFilter.class);
}
#Bean
AbstractAuthenticationProcessingFilter tokenAuthenticationFilter() throws Exception {
final AbstractAuthenticationProcessingFilter filter = new TokenAuthenticationFilter(PROTECTED_URLS);
filter.setAuthenticationManager(authenticationManager());
filter.setAuthenticationSuccessHandler(successHandler());
// maybe error handling to provide some custom response?
return filter;
}
// it's critically important to register your filter properly in spring context
/** Disable Spring boot automatic filter registration. */
#Bean
FilterRegistrationBean disableRegistrationForAuthenticationFilter(final TokenAuthenticationFilter filter) {
final FilterRegistrationBean registration = new FilterRegistrationBean(filter);
registration.setEnabled(false);
return registration;
}
// this one also is critically important to avoid redirection
#Bean
SimpleUrlAuthenticationSuccessHandler successHandler() {
final SimpleUrlAuthenticationSuccessHandler successHandler = new SimpleUrlAuthenticationSuccessHandler();
successHandler.setRedirectStrategy(new NoRedirectStrategy());
return successHandler;
}
}
You can store usernames and passwords in database, which you can use to login users. You create your own class which extends WebSecurityConfigurerAdapter and override methods which you need to modify:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
DataSource dataSource;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
auth.jdbcAuthentication()
.dataSource(dataSource)
}
}
But be vary of Spring Security default database query when searching for usernames and passwords so you can create database schema which will be good:
public static final String DEF_USERS_BY_USERNAME_QUERY =
"select username,password,enabled " +
"from users " +
"where username = ?";
public static final String DEF_AUTHORITIES_BY_USERNAME_QUERY =
"select username,authority " +
"from authorities " +
"where username = ?";
public static final String DEF_GROUP_AUTHORITIES_BY_USERNAME_QUERY =
"select g.id, g.group_name, ga.authority " +
"from groups g, group_members gm, group_authorities ga " +
"where gm.username = ? " +
"and g.id = ga.group_id " +
"and g.id = gm.group_id";
But you can also use Spring methods to specify your own query to database:
auth
.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery(
"select username, password, enabled from Users " +
"where username=?")
You should POST your data to some service you created which will store user and pass to a database.

Spring Boot Basic Auth for each request, username validated for later requests

I have enabled the http basic auth in spring boot. I am seeing strange results when calling from Postman
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private ApiUserDetailsService userDetails;
#Bean
public ShaPasswordEncoder passwordEncoder() {
return new ShaPasswordEncoder();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
ReflectionSaltSource salt = new ReflectionSaltSource();
salt.setUserPropertyToUse("username");
DaoAuthenticationProvider dao = new DaoAuthenticationProvider();
dao.setUserDetailsService(userDetails);
dao.setPasswordEncoder(passwordEncoder());
dao.setSaltSource(salt);
auth.authenticationProvider(dao);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().and().csrf().disable().httpBasic();
}
Custome Userdetails
#Service
public class ApiUserDetailsService implements UserDetailsService {
#Autowired
private JdbcTemplate jdbcTemplate;
#Value("${spring.queries.users.query}")
private String usersQuery;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
List<UserDetails> usersBasedOnUserName = getUsersBasedOnUserName(username);
if (usersBasedOnUserName.size() == 0) {
throw new UsernameNotFoundException("Username " + username + " not found");
}
return usersBasedOnUserName.get(0);
}
private List<UserDetails> getUsersBasedOnUserName(String username) {
return jdbcTemplate.query(this.usersQuery, new String[] {username}, new RowMapper<UserDetails>() {
#Override
public UserDetails mapRow(ResultSet rs, int rowNum) throws SQLException {
String username = rs.getString(1);
String password = rs.getString(2);
return new User(username, password, AuthorityUtils.NO_AUTHORITIES);
}
});
}
}
For the fist time I execute the request, it expects the correct credentials. After I enter correct credentials, I get the result. But when I try for the the same request without credentials or diffrent password keepign username same, I wont get 401 error.
I will get 401 error only when I chnage username.
My API needs to be validated against each request.
Am I doing some thing wrong here.
Adding the stateless to config helped to solve issue.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.authorizeRequests().anyRequest().authenticated().and().csrf().disable().httpBasic();
}

Custom AuthenticationProvider is not called

I want to have a basic auth-protected REST app. I followed the general instructions from http://www.baeldung.com/spring-security-authentication-provider in order to get the security working.
I ended up creating my implementation of AuthenticationProvider, but it never gets called by Spring. All requests end up with an error:
{"timestamp":1460199213227,"status":401,"error":"Unauthorized","message":"Full authentication is required to access this resource","path":"/test"}
without the AuthenticationProvider ever doing anything.
The app is annotation-based and here are the relevant bits:
Security setup
#Configuration
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class ApplicationSecurity extends WebSecurityConfigurerAdapter {
#Autowired
CustomAuthenticationProvider authenticationProvider;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authenticationProvider(authenticationProvider)
.authorizeRequests()
.anyRequest().authenticated().and().httpBasic();
}
}
AuthenticationProvider
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Autowired
private UserDAO userDAO;
#Autowired
private Authenticator authenticator;
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// This never gets called, I checked with debugger
String username = authentication.getName();
String password = authentication.getCredentials().toString();
User user = userDAO.findByUsername(username);
User authenticatedUser = authenticator.authenticate(user, password);
if (authenticatedUser == null){
throw new RESTAuthenticationException("Auth failed");
}
List<GrantedAuthority> authorityList = new ArrayList<>();
return new UsernamePasswordAuthenticationToken(user, authorityList);
}
#Override
public boolean supports(Class<?> aClass) {
return aClass.equals(UsernamePasswordAuthenticationToken.class);
}
}
Controller
#RestController
public class UserController {
#RequestMapping(value = "/test")
public ResponseEntity test(#AuthenticationPrincipal User user) {
return ResponseEntity.ok().body(user);
}
}
You receive a response with status code 401. This is the "unauthorized" http status code. It is probably caused by a missing/malformed Authorization header in your request.
You are using Http-Basic: it requires the following header in the request :
Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l
where the string QWxhZGRpbjpPcGVuU2VzYW1l is the string <user>:<password> base64 encoded.

Resources