How to check that BadCredentialsException is not thrown in loadUserByUsername - spring

i am authenticating my users with UserDetailsService:
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="userDetailsService">
<password-encoder hash="sha"/>
</authentication-provider>
</authentication-manager>
userDetailsService class:
#Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
private UserService userService;
#Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException, DataAccessException {
User user = null;
try {
user = userService.getUserByUsername(username);
} catch (Exception e) {
e.printStackTrace();
}
if (user.isForceChangePass()) {
MyForcePasswordChangeException bad = new MyForcePasswordChangeException(
"Password is not valid, and it must be changed!");
throw bad;
}
}
EDIT:
after getting username i check for ForceChangePass indicator and if it's true i through my own exception which in turns lands user to loginFailureHandler (despite password is correct or not) i want in the loginFailureHandler to check if my exception is thrown in case of login success only.

loadUserByUsername() is not suppose to check credentials, it should only load UserDetails object (having getPassword() method) or throw UsernameNotFoundException.
If you want to check whether the user successfully authenticated or not, have a look at Listening to successful login with Spring Security:
<form-login
authentication-success-handler-ref="authenticationSuccessHandler"
authentication-failure-url="authenticationFailureHandler"/>
You must implement AuthenticationSuccessHandler and AuthenticationFailureHandler.
Alternatively consider subclassing BasicAuthenticationFilter and override onSuccessfulAuthentication() and onUnsuccessfulAuthentication().

Related

x509 validation fails before it can be captured

I have a Spring Boot application, using x509 authentication which further validates users against a database. When a user accesses the site, internal Spring code calls the loadUserByUsername method which in turn makes the database call. This all happens before the controller is aware anything is happening. If the user is not found it throws an EntityNotFoundException and displays the stack trace on the user's browser.
I'm using Spring Boot Starter. The controller has code to capture the exception and return a 'Not Authorized' message, but this happens long before. Has anyone else seen this and do you have a workaround?
#Service
public class UserService implements UserDetailsService {
public UserDetails loadUserByUsername(String dn) {
ApprovedUser details = auService.getOne(dn);
if (details == null){
String message = "User not authorized: " + dn;
throw new UsernameNotFoundException(message);
}
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
if (details.isAdminUser()){
authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN_USER"));
}
return new AppUser(dn, "", authorities);
}
Usually, you would use a AuthenticationFailureHandler to encapsulate logic that's triggered by an AuthenticationException. The X509AuthenticationFilter would usually call PreAuthenticatedAuthenticationProvider to authenticate, which would in turn invoke the loadUserByUsername(...) method from UserDetailsService. Any AuthenticationException thrown by the UserDetailsService is caught by the filter and control is passed to the registered AuthenticationFailureHandler. This includes UsernameNotFoundException.
However, if you're using the X509Configurer, (http.x509()) there is no way of setting a handler directly on the filter. So once the exception is thrown, X509AuthenticationFilter catches it, sees that there's no default handler, and then simply passes the request to the next filter in the filter chain.
One way to get around this could be to provide a custom X509AuthenticationFilter.
In WebSecurityConfigurerAdapter:
#Autowired
private AuthenticationFailureHandler customFailureHandler;
#Autowired
private UserService customUserService;
#Bean(name = BeanIds.AUTHENTICATION_MANAGER)
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
protected void configure(HttpSecurity http) throws Exception {
...
http.x509().x509AuthenticationFilter(myX509Filter())
.userDetailsService(customUserService)
...
}
private X509AuthenticationFilter myX509Filter() {
X509AuthenticationFilter myCustomFilter = new X509AuthenticationFilter();
myCustomFilter.setAuthenticationManager(authenticationManagerBean());
...
myCustomFilter.setContinueFilterChainOnUnsuccessfulAuthentication(false);
myCustomFilter.setAuthenticationFailureHandler(customFailureHandler);
return myCustomFilter;
}
Then you can write your own AuthenticationFailureHandler implementation and expose it as a bean.

Where to put custom post-authentication code using UsernamePasswordAuthenticationFilter

I'm using Spring and custom implementation of UsernamePasswordAuthenticationFilter. I want to perform some custom code after successful authentication (for example: log a message with username that just got authenticated).
Which method should I override or how to register a handler for successful authentication ?
Is it good idea to override successfulAuthentication() method, put there my custom code and finish it with call to original method (super.successfulAuthentication();) ? Or there is some other best practise?
My approach for performing custom tasks after a successful
authentication is to use a Custom Authentication Success Handler in
Spring Security.
You can achieve this as below:
Create your custom AuthenticationSuccessHandler like TMGAuthenticationSuccessHandler. I have created a sample code which redirects user to the password change page, if the user is detected to be using the default machine generated password.
#Component("tMGAuthSuccessHandler")
public class TMGAuthSuccessHandler implements AuthenticationSuccessHandler {
private AuthenticationSuccessHandler target = new SavedRequestAwareAuthenticationSuccessHandler();
#Autowired
private UserService userService;
private static final Logger LOGGER = LoggerFactory.getLogger(TMGAuthSuccessHandler.class);
#Override
public void onAuthenticationSuccess(HttpServletRequest servletRequest, HttpServletResponse servletResponse, Authentication authentication)
throws IOException, ServletException {
if (hasDefaultPassword(authentication)) {
LOGGER.debug("Default password detected for username: " + authentication.getName());
servletResponse.sendRedirect("changePassword");
} else {
target.onAuthenticationSuccess(servletRequest, servletResponse, authentication);
}
}
/**
* Checks whether default password is used in login.
*/
private boolean hasDefaultPassword(Authentication authentication) {
String username = authentication.getName();
User user = userService.findOnUsername(username, true, false, false, false);
if (user != null && user.getLoginAuditTrail() != null && user.getLoginAuditTrail().isDefaultPasswordUsed() != null) {
return user.getLoginAuditTrail().isDefaultPasswordUsed();
}
return false;
}
/**
* Proceeds to the requested URL.
*/
public void proceed(HttpServletRequest servletRequest, HttpServletResponse servletResponse, Authentication authentication) throws IOException,
ServletException {
target.onAuthenticationSuccess(servletRequest, servletResponse, authentication);
}
}
Modify the securityContext.xml or similar file that contains spring security related configurations. Add this customHander to http configuration as authentication-success-handler-ref="tMGAuthSuccessHandler". Code snippet is shown below:
<security:http use-expressions="true" authentication-manager-ref="webAppAuthManager">
<!-- signin and signout -->
<security:intercept-url pattern="/signin" access="permitAll" />
<security:intercept-url pattern="/logout" access="permitAll" />
<security:intercept-url pattern="/accessDenied" access="permitAll"/>
<security:intercept-url pattern="/**" access="isAuthenticated()" />
<!-- sign in Configuration -->
<security:form-login login-page="/signin"
username-parameter="username"
password-parameter="password"
authentication-failure-url="/signin?authFail=true"
authentication-success-handler-ref="inoticeAuthSuccessHandler" />
<security:logout logout-url="/signout" invalidate-session="true" delete-cookies="JSESSIONID" logout-success-url="/signin?logout=true" />
</security:http>
You are good to go now.
Reference credit: How to use custom filter with authentication-success-handler-ref equivalent in spring security
You have at least two options:
AuthenticationSuccessHandler
ApplicationListener<E extends ApplicationEvent> with AbstractAuthenticationEvent

How to get Authentication inside UserDetailsService subclass

I was using custom auth like this
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException{
LoggedInUser user = new LoggedInUser();
...
...
return new UsernamePasswordAuthenticationToken(user, authentication.getCredentials());
}
here i was setting some value to authentication object
Now i am using password-encoder so my custom authentication is
#Service
public class CustomUserDetailsService implements UserDetailsService {
#Autowired
private UserService userService;
static final PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
#Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
}
Here inside loadUserByUsername how can i set anything to Authentication object ?
UserDetailsService implementations should be responsible for retrieving user details based on provided ID and not to manipulate Authentication. To process a specific Authentication object, implement custom AuthenticationProvider or extend an existing one (e.g. DaoAuthenticationProvider) that will delegate to your UserDetailsService to retrieve a user. Such a user can be then assigned to the authentication object and returned from AuthenticationProvider.authenticate(...) method.
Look at Spring Security's DaoAuthenticationProvider that is implemented this way applying the single responsibility and separation of concerns principles.

How to handle exceptions properly in custom Spring security 3.0 authentication?

I'm developing a REST service based in tokens. When an user goes to ../rest/authenticate with the user and password via curl, gets a valid token in order to use the whole API.
My problem appears when the user forgets to insert the username, the password or the token in the other methods because i've not managed to handle the Authentication exceptions as I want.
I cand handle the exceptions but tomcat gets the response and inserts some html that I don't expect.
This is the typical response of tomcat.
Is it possible to receive a response like 200 OK which don't have this html code?
At the momment, this is my config:
AuthenticationProcessingFilter
Decides if the url is secured or not. If has to be secured, calls the authentication manager in order to validate it. If receives an authentication exceptions calls the AuthenticationEntryPoint
public class AuthenticationTokenProcessingFilter extends GenericFilterBean {
private final Collection<String> nonTokenAuthUrls = Lists.newArrayList("/rest","/rest/authenticate");
TokenAuthenticationManager tokenAuthenticationManager;
RestAuthenticationEntryPoint restAuthenticationEntryPoint;
public AuthenticationTokenProcessingFilter(TokenAuthenticationManager tokenAuthenticationManager, RestAuthenticationEntryPoint restAuthenticationEntryPoint) {
this.tokenAuthenticationManager = tokenAuthenticationManager;
this.restAuthenticationEntryPoint = restAuthenticationEntryPoint;
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest)request;
HttpServletResponse httpResponse = (HttpServletResponse)response;
try{
if(!nonTokenAuthUrls.contains(httpRequest.getRequestURI())){ //Auth by token
String hash = httpRequest.getHeader("token");
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(hash, null);
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails((HttpServletRequest) request));
SecurityContextHolder.getContext().setAuthentication(tokenAuthenticationManager.authenticate(authentication));
}
response.reset();
chain.doFilter(request, response);
}catch(AuthenticationException authenticationException){
SecurityContextHolder.clearContext();
restAuthenticationEntryPoint.commence(httpRequest, httpResponse, authenticationException);
}
}
AuthenticationManager
public class TokenAuthenticationManager implements AuthenticationManager{
#Autowired
UserService userService;
#Autowired
TokenService tokenService;
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
Object hash = authentication.getPrincipal();
if(hash == null)
throw new BadCredentialsException("Token is required");
User user = tokenService.getUserFromTokenHash((String)hash);
if(user == null)
throw new BadCredentialsException("Non-existent token");
if(!tokenService.validate((String)hash))
throw new BadCredentialsException("Expired Token");
org.springframework.security.core.userdetails.User userDetails = new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), getUserGrantedAuthorities(user.getRoles()));
return new UsernamePasswordAuthenticationToken(userDetails, user.getPassword(), getUserGrantedAuthorities(user.getRoles()));
}
AuthenticationEntryPoint
This class works OK. The code received is 401 unauthorized but the message is in the tomcat html
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authenticationException) throws IOException, ServletException {
response.setContentType("application/json");
response.sendError( HttpServletResponse.SC_UNAUTHORIZED, authenticationException.getMessage() );
response.getOutputStream().println("{ \"error\": \"" + authenticationException.getMessage() + "\" }");
}
}
The RestAccessDeniedHanler is not called either. It's difficult becasue there are a lot of classes that have to be implemented.
I reviewed some post in stackoverflow and other websites and my approach consist on catching the exceptions in the AuthenticationProcessingFilter and call the AuthenticationEntryPoint manually. I decided to do that becasue I've tried to configure this in the applicationContext-security.xml with no success.
appliacionContext-security.xml
<b:bean id="restAuthenticationEntryPoint" class="...web.security.RestAuthenticationEntryPoint" />
<b:bean id="tokenAuthenticationManager" class="...dp.web.security.TokenAuthenticationManager"/>
<b:bean id="AuthenticationTokenProcessingFilter" class="...web.security.AuthenticationTokenProcessingFilter">
<b:constructor-arg type="...dp.web.security.TokenAuthenticationManager" ref="tokenAuthenticationManager"></b:constructor-arg>
<b:constructor-arg type="...dp.web.security.RestAuthenticationEntryPoint" ref="restAuthenticationEntryPoint"></b:constructor-arg>
</b:bean>
<b:bean id="accessDeniedHandler" class="...dp.web.security.RestAccessDeniedHandler">
</b:bean>
<http realm="Protected REST API" pattern="/rest/**" use-expressions="true" auto-config="false" create-session="stateless" entry-point-ref="restAuthenticationEntryPoint">
<custom-filter ref="AuthenticationTokenProcessingFilter" position="FORM_LOGIN_FILTER" />
<access-denied-handler ref="accessDeniedHandler"/>
</http>
how can I send a clean response with the error code and a message?
You can use error-pages in your web.xml to intercept Tomcat's error page. For example,
<error-page>
<error-code>404</error-code>
<location>/404</location>
</error-page>
Now you use RequestMapping to map /404 to a page that returns your JSON response without any HTML:
#RequestMapping(value = "/404", method = {RequestMethod.GET, RequestMethod.POST, RequestMethod.PUT, RequestMethod.DELETE})
#ResponseBody
public ResponseEntity<ResponseStatus> handle404() {
HttpStatus status = null;
ResponseStatus responseStatus = new ResponseStatus("404", "Wrong path to resource.");
status = HttpStatus.NOT_FOUND;
ResponseEntity<ResponseStatus> response = new ResponseEntity<ResponseStatus>(responseStatus, status);
return response;
}
Which will simply return a JSON object called Response Status that contains an error code and error message as fields.

Spring MVC, Spring Security, Simply Require Username

I have a Spring 3 MVC website using Spring Security 3.1.0RC2. Currently I am using the org.springframework.security.ldap.authentication.ad.ActiveDirectoryAuthenticationProvider for log-in. For demo purposes, my boss just wants to have to enter a username (any username) and not have it validated against anything, instead, just grant access. Is there some way I can make sure the user entered a username and that's it?
Why not just write your own AuthenticationProvider.
class MyAuthProvider implements AuthenticationProvider {
public Authentication authenticate(Authentication authentication) {
// add code to populate GrantedAuthority list if needed.
Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new GrantedAuthorityImpl("ROLE_1"));
authorities.add(new GrantedAuthorityImpl("ROLE_2"));
authorities.add(new GrantedAuthorityImpl("ROLE_3"));
return new UsernamePasswordAuthenticationToken(
authentication.getPrincipal(),
authentication.getCredentials(),
authorities);
}
public boolean supports(Class<?> authentication) {
return true;
}
}
In your spring security config:
<security:authentication-manager>
<security:authentication-provider ref="myAuthenticationProvider" />
</security:authentication-manager>
<bean id="myAuthenticationProvider" class="MyAuthProvider"/>
UPDATE 1
See above for how to add authorities (granted roles) to the user.

Resources