Spring MVC, Spring Security, Simply Require Username - spring

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.

Related

Spring security providers precedence

I have this configuration, where activeDirectoryAuthenticationProvider is a customized Active Directory provider. What I want to achieve, is that if database authentication fails, no further authentications are attempted.
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
JdbcUserDetailsManager udm = jdbcUserDetailsManager(ds);
udm.setEnableGroups(true);
udm.setEnableAuthorities(false);
auth.userDetailsService(udm).passwordEncoder(userPasswordEncoder())
.and().authenticationProvider(activeDirectoryAuthenticationProvider);
}
Current scenario is:
I have a users databse with PK on the user name
I have a database user with usernname user with some password with some permissions assigned
I have an unrelated user user on active directory with a different password
I login with the user and the password from Active Directory
The user is logged in and gets the permissions from the database user
What I want is:
Login fails, since database authentication has precedence over any other method (that's a biz requirement)
Is this achievable? How could it be done?
SpringSecurity default providers chain don't know anything about priority of providers. Spring tries to authenticate via each provider until someone return Authentication object.
You need custom implementation of AuthenticationProvider, something like PrimaryOrientedAuthenticationProvider. I had a similar case. My implementitaion:
public class PrimaryOrientedAuthenticationProvider implements AuthenticationProvider {
private final AuthenticationProvider primaryAuthenticationProvider;
private final AuthenticationProvider secondaryAuthenticationProvider;
#Override
public Authentication authenticate(Authentication authentication) {
Authentication auth;
try {
auth = primaryAuthenticationProvider.authenticate(authentication);
} catch (UsernameNotFoundException | InternalAuthenticationServiceException ex) {
log.debug("Trying to authenticate with secondary provider after exception", ex);
return secondaryAuthenticationProvider.authenticate(authentication);
}
if (auth == null) {
log.debug("Trying to authenticate with secondary provider after no primary one was returned");
return secondaryAuthenticationProvider.authenticate(authentication);
}
return auth;
}
#Override
public boolean supports(Class<?> authentication) {
return primaryAuthenticationProvider.supports(authentication) &&
secondaryAuthenticationProvider.supports(authentication);
}
}
So, activeDirectoryAuthenticationProvider will try to authenticate only if databaseAuthenticationProvider will throw UsernameNotFoundException (user does not exist) or InternalAuthenticationServiceException (database not available for example).

Spring secure endpoint with only client credentials (Basic)

I have oauth2 authorization server with one custom endpoint (log out specific user manually as admin)
I want this endpoint to be secured with rest client credentials (client id and secret as Basic encoded header value), similar to /oauth/check_token.
This endpoint can be called only from my resource server with specific scope.
I need to check if the client is authenticated.
I would like to be able to add #PreAuthorize("#oauth2.hasScope('TEST_SCOPE')")on the controller`s method.
I could not find any docs or way to use the Spring`s mechanism for client authentication check.
EDIT 1
I use java config not an xml one
So I ended up with the following solution
Authentication Manager
public class ClientAuthenticationManager implements AuthenticationManager {
private ClientDetailsService clientDetailsService;
private PasswordEncoder passwordEncoder;
public HGClientAuthenticationManager(ClientDetailsService clientDetailsService, PasswordEncoder passwordEncoder) {
Assert.notNull(clientDetailsService, "Given clientDetailsService must not be null!");
Assert.notNull(passwordEncoder, "Given passwordEncoder must not be null!");
this.clientDetailsService = clientDetailsService;
this.passwordEncoder = passwordEncoder;
}
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
ClientDetails clientDetails = null;
try {
clientDetails = this.clientDetailsService.loadClientByClientId(authentication.getPrincipal().toString());
} catch (ClientRegistrationException e) {
throw new BadCredentialsException("Invalid client id or password");
}
if (!passwordEncoder.matches(authentication.getCredentials().toString(), clientDetails.getClientSecret())) {
throw new BadCredentialsException("Invalid client id or password");
}
return new OAuth2Authentication(
new OAuth2Request(null, clientDetails.getClientId(), clientDetails.getAuthorities(), true,
clientDetails.getScope(), clientDetails.getResourceIds(), null, null, null),
null);
}
}
Filter declaration
private BasicAuthenticationFilter basicAuthenticationFilter() {
ClientDetailsUserDetailsService clientDetailsUserDetailsService = new ClientDetailsUserDetailsService(
this.clientDetailsService);
clientDetailsUserDetailsService.setPasswordEncoder(this.passwordEncoder);
return new BasicAuthenticationFilter(
new ClientAuthenticationManager(this.clientDetailsService, this.passwordEncoder));
}
Filter registration
httpSecurity.addFilterBefore(this.basicAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
WARNING!!!
This will prevent any other types of authentication (oauth2, etc.).
ONLY Basic authentication is accepted and ONLY for registered clients.
#PreAuthorize("#oauth2.hasScope('TEST_SCOPE')") On the controller method should be sufficiƫnt. If the client is not authenticated, no scope is available and the scope check will fail.
If you want, you can use the Spring Security expression #PreAuthorize("isAuthenticated()") to check if a client is authenticated: https://docs.spring.io/spring-security/site/docs/5.0.0.RELEASE/reference/htmlsingle/#el-common-built-in
You could also configure the HttpSecurity instead of working with #PreAuthorize

How to retrive user entered password in UserDetailsService

Spring security 3 may do some trick to validate user's password behind the scene, but that's become my problem right now, I am trying to intercept whatever entered for password by user, and just couldn't find a clue.
#Component("customUserDetailsService")
public class CustomUserDetailsService implements UserDetailsService {
............
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
User user = userService.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("User '"+username+"' not found !");
}
return user;
}
}
is there any API that I can use to intercept the user's password?
The UserDetailsService is responsible to load the user and provide a UserDetails object that contains the password stored in the database. Unfortunaly (for you) this password is hashed (SHA or MD5) in the most cases.
If you want to intercept the password that is entered by the user, then you have different choices:
The UserNamePasswordFilter (when you use Form Authentication, if you use an other kind of authentication, then you need an other filter) ins one point to intercept the password. It is responsible to fetch the login http request, create a UserNamePasswordAuthenticationToken and forward them to the AuthenticationManager.
An other interception point would be the AuthenticationManager (more precise the ProviderManager - then only real implemenation of the AuthenticationManager). It has a method Authentication authenticate(Authentication authentication) that take the user input (Subclass of Authentication for example UsernamePasswordAuthenticationToken) and verifiy it (by forwarding it to an AuthenticationProvider)
The AuthenticationProvider (for example the DaoAuthenticationProvider) would be an other place to intercept the password.
The DaoAuthenticationProvider uses a PasswordEncoder to hash the user entered password. Then the DaoAuthenticationProvider will compare the hash password obtained from the database with the hashed password entered by the user. So the PasswordEncoder is probably the easiest way to intercept the user entered password!
And of course you can intercept the HttpRequest itself: eighter you register an additional SecurityFilter (before the UsernamePasswordFilter) or a simple Servlet Filter (before the Spring Security Filter). (A Spring Interceptor will not work, because the Spring Security Filter will handle the request an will not forward it to the Spring Dispatcher, so the Spring Dispatcher can not invoke the Spring Interceptor.)
password encoder registration:
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider user-service-ref="jdbcUserService">
<sec:password-encoder ref="myPasswordEncoder"/>
</sec:authentication-provider>
</sec:authentication-manager>
<beans:bean id="myPasswordEncoder"class="InterceptingPassordEncoderSubclassShaPasswordEncoder" />

How to check that BadCredentialsException is not thrown in loadUserByUsername

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().

Auto login after successful registration

hey all
i want to make an auto login after successful registration in spring
meaning:
i have a protected page which requires login to access them
and i want after registration to skip the login page and make an auto login so the user can see that protected page, got me ?
i am using spring 3.0 , spring security 3.0.2
how to do so ?
This can be done with spring security in the following manner(semi-psuedocode):
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.savedrequest.SavedRequest;
#Controller
public class SignupController
{
#Autowired
RequestCache requestCache;
#Autowired
protected AuthenticationManager authenticationManager;
#RequestMapping(value = "/account/signup/", method = RequestMethod.POST)
public String createNewUser(#ModelAttribute("user") User user, BindingResult result, HttpServletRequest request, HttpServletResponse response) {
//After successfully Creating user
authenticateUserAndSetSession(user, request);
return "redirect:/home/";
}
private void authenticateUserAndSetSession(User user, HttpServletRequest request) {
String username = user.getUsername();
String password = user.getPassword();
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
// generate session if one doesn't exist
request.getSession();
token.setDetails(new WebAuthenticationDetails(request));
Authentication authenticatedUser = authenticationManager.authenticate(token);
SecurityContextHolder.getContext().setAuthentication(authenticatedUser);
}
}
Update: to only contain how to create the session after the registration
In Servlet 3+ you can simply do request.login("username","password") and if successful, redirect to whatever page you want. You can do the same for auto logout.
Here is the link to the section of the documentation that talks about this: http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#servletapi-3
Just a comment to the first reply on how to autowire authenticationManager.
You need to set an alias when you declare authentication-manager in either your applicantion-servlet.xml or applicationContext-security.xml file:
<authentication-manager alias="authenticationManager>
<authentication-provider>
<user-service>
<user name="jimi" password="jimispassword" authorities="ROLE_USER, ROLE_ADMIN" />
<user name="bob" password="bobspassword" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
Also, when you authenticate, it may throw an AuthenticationException, so you need to catch it:
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(user.getEmail(), user.getPassword());
request.getSession();
token.setDetails(new WebAuthenticationDetails(request));
try{
Authentication auth = authenticationManager.authenticate(token);
SecurityContextHolder.getContext().setAuthentication(auth);
} catch(Exception e){
e.printStackTrace();
}
return "redirect:xxxx.htm";
Configure web.xml to allow Spring Security to handle forwards for a login processing url.
Handle registration request, e.g. create user, update ACL, etc.
Forward it with username and password to login processing url for authentication.
Gain benefits of entire Spring Security filter chain, e.g. session fixation protection.
Since forwards are internal, it will appear to the user as if they are registered and logged in during the same request.
If your registration form does not contain the correct username and password parameter names, forward a modified version of the request (using HttpServletRequestWrapper) to the Spring Security login endpoint.
In order for this to work, you'll have to modify your web.xml to have the Spring Security filter chain handle forwards for the login-processing-url. For example:
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<!-- Handle authentication for normal requests. -->
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Handle authentication via forwarding for internal/automatic authentication. -->
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/login/auth</url-pattern>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
Source: mohchi blog
I incorporated the same scenario, below is the code snippet. To get the instance of AuthenticationManager, you will need to override the authenticationManagerBean() method of WebSecurityConfigurerAdapter class
SecurityConfiguration(extends WebSecurityConfigurerAdapter)
#Bean(name = BeanIds.AUTHENTICATION_MANAGER)
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
Controller
#Autowired
protected AuthenticationManager authenticationManager;
#PostMapping("/register")
public ModelAndView registerNewUser(#Valid User user,BindingResult bindingResult,HttpServletRequest request,HttpServletResponse response) {
ModelAndView modelAndView = new ModelAndView();
User userObj = userService.findUserByEmail(user.getEmail());
if(userObj != null){
bindingResult.rejectValue("email", "error.user", "This email id is already registered.");
}
if(bindingResult.hasErrors()){
modelAndView.setViewName("register");
return modelAndView;
}else{
String unEncodedPwd = user.getPassword();
userService.saveUser(user);
modelAndView.setViewName("view_name");
authWithAuthManager(request,user.getEmail(),unEncodedPwd);
}
return modelAndView;
}
public void authWithAuthManager(HttpServletRequest request, String email, String password) {
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(email, password);
authToken.setDetails(new WebAuthenticationDetails(request));
Authentication authentication = authenticationManager.authenticate(authToken);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
Using SecurityContextHolder.getContext().setAuthentication(Authentication) gets the job done but it will bypass the spring security filter chain which will open a security risk.
For e.g. lets say in my case when user reset the password, I wanted him to take to the dashboard without login again. When I used the above said approach, it takes me to dashboard but it bypassed my concurrency filter which I have applied in order to avoid concurrent login. Here is the piece of code which does the job:
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(empId, password);
Authentication auth = authenticationManager.authenticate(authToken);
SecurityContextHolder.getContext().setAuthentication(auth);
Use login-processing-url attribute along with a simple change in web.xml
security-xml
<form-login login-page="/login"
always-use-default-target="false"
default-target-url="/target-url"
authentication-failure-url="/login?error"
login-processing-url="/submitLogin"/>
web.xml
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/submitLogin</url-pattern>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
By adding this piece of code in web.xml actually does the job of forwarding your explicit forward request which you will make during auto login and passing it to the chain of spring security filters.
Hope it helps
This is an alternative to the Servlet 3+ integration. If you're using Spring Security's form login, then you can simply delegate to your login page. For example:
#PostMapping("/signup")
public String signUp(User user) {
// encode the password and save the user
return "forward:/login";
}
Assuming you have username and password fields in your form, then the 'forward' will send those parameters and Spring Security will use those to authenticate.
The benefit I found with this approach is that you don't duplicate your formLogin's defaultSuccessUrl (example security setup below). It also cleans up your controller by not requiring a HttpServletRequest parameter.
#Override
public void configure(HttpSecurity http) {
http.authorizeRequests()
.antMatchers("/", "/signup").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/home", true)
.permitAll();
}
Spring Monkey's answer works great but I encountered a tricky problem when implementing it.
My problem was because I set the registration page to have "no security", eg:
<http pattern="/register/**" security="none"/>
I think this causes no SecurityContext initialized, and hence after user registers, the in-server authentication cannot be saved.
I had to change the register page bypass by setting it into IS_AUTHENTICATED_ANONYMOUSLY
<http authentication-manager-ref="authMgr">
<intercept-url pattern="/register/**" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
...
</http>
This is answer to above question
In Controller:
#RequestMapping(value = "/registerHere", method = RequestMethod.POST)
public ModelAndView registerUser(#ModelAttribute("user") Users user, BindingResult result,
HttpServletRequest request, HttpServletResponse response) {
System.out.println("register 3");
ModelAndView mv = new ModelAndView("/home");
mv.addObject("homePagee", "true");
String uname = user.getUsername();
if (userDAO.getUserByName(uname) == null) {
String passwordFromForm = user.getPassword();
userDAO.saveOrUpdate(user);
try {
authenticateUserAndSetSession(user, passwordFromForm, request);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("register 4");
log.debug("Ending of the method registerUser");
return mv;
}
Further above method in controller is defined as:
`private void authenticateUserAndSetSession(Users user, String passwor`dFromForm, HttpServletRequest request){
String username = user.getUsername();
System.out.println("username: " + username + " password: " + passwordFromForm);
UserDetails userDetails = userDetailsService.loadUserByUsername(user.getUsername());
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(username, passwordFromForm, userDetails.getAuthorities());
request.getSession();
System.out.println("Line Authentication 1");
usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetails(request));
System.out.println("Line Authentication 2");
Authentication authenticatedUser = authenticationManager.authenticate(usernamePasswordAuthenticationToken);
System.out.println("Line Authentication 3");
if (usernamePasswordAuthenticationToken.isAuthenticated()) {
SecurityContextHolder.getContext().setAuthentication(authenticatedUser);
System.out.println("Line Authentication 4");
}
request.getSession().setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext());// creates context for that session.
System.out.println("Line Authentication 5");
session.setAttribute("username", user.getUsername());
System.out.println("Line Authentication 6");
session.setAttribute("authorities", usernamePasswordAuthenticationToken.getAuthorities());
System.out.println("username: " + user.getUsername() + "password: " + user.getPassword()+"authorities: "+ usernamePasswordAuthenticationToken.getAuthorities());
user = userDAO.validate(user.getUsername(), user.getPassword());
log.debug("You are successfully register");
}
Other answers didnt suggest to put it in try/catch so one does not realize why logic is not working as code runs...and nothing is there neither error or exception on console. So if you wont put it in try catch you wont get exception of bad credentials.
I'm not sure if you are asking for this, but in your Spring Security configuration you can add a "remember-me" tag. This will manage a cookie in your client, so next time (if the cookie hasn't expired) you'll be logged automatically.
<http>
...
<remember-me />
</http>

Resources