Reputation: 9205
An app currently uses spring security with a simple username and password handled by a login form that spring generates automatically. The username is a valid email address.
I have other code that sends the user an email with a session id in a url to validate the email address. And that also sends the user a text message with a custom pin code to validate their phone number. The steps of registering for the site, validating email, and validating phone number require several controller methods and steps.
Is there a quick and easy way to add code to an existing controller method that simply declares the user as logged in? This would use the existing spring security configurations, and simply augment by declaring the user as logged in.
For example, in a controller method:
//confirmed email
//confirmed phone number
//confirmed password
loggedin = true;
CLARIFICATION:
The confirmation text pin code and confirmation email need to be sent and validated every time the user logs in. If it is not possible to log someone in with a simple programmatic statement, can I at least change the user's role with a simple programmatic statement? So they log in to limited, conditional status with username and password and they get assigned "StepOneRole". Then they validate the pin code that is sent to them and they get "StepTwoRole". Then they validate an email link sent to them, and their role is changed to "FullUser", in which they can actually use the secured parts of the site.
I am trying to avoid adding unnecessary complexity, but the user needs to validate n factors every time.
Also, note that my SecurityConfig.java
is as follows, and uses UserDetailService
, which is mentioned in one of the answers below:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
@Configuration
@EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/user-home")
.usernameParameter("j_username")
.passwordParameter("j_password")
.loginProcessingUrl("/j_spring_security_check")
.failureUrl("/login")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login")
.and()
.authorizeRequests()
.antMatchers("/user-home").hasAuthority("USER")
.antMatchers("/j_spring_security_check").permitAll()
.and()
.userDetailsService(userDetailsService);
}
}
Upvotes: 0
Views: 2491
Reputation: 8985
The following code would log a user in programmatically:
Authentication authentication =
new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
Note that user
above it the user object, implementing UserDetails
.
Similarly, to log a user out, the following code can be used:
SecurityContextHolder.getContext().setAuthentication(null);
Response to your comment:
I am not sure how Spring security would be able to use your user and roles without your user class or some mapping class implementing UserDetails
. In one of my projects, I have a mapping class like this:
public class UserDetailsImpl implements UserDetails {
private static final long serialVersionUID = 5197941260523577515L;
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public UserDetailsImpl(User user) {
this.user = user;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> authorities = new HashSet<GrantedAuthority>(
user.getRoles().size() + 1);
for (Role role : user.getRoles())
authorities.add(new SimpleGrantedAuthority("ROLE_" + role.name()));
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
return authorities;
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getEmail();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
So, I create an instance of it from my user object, and use that to log in programmatically, like this:
UserDetailsImpl userDetails = new UserDetailsImpl(user);
Authentication authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
Response to third comment:
Yes, User
is a self coded class. Here is the link to a zip containing the source of the entire project. It's a list of chapter-wise source of a video tutorial, but you need to look only at the last in the list.
Upvotes: 3
Reputation: 869
no quick and easy if you want more parameter to spring security log-in but its not that bad.
Spring security allows you decide anything you want when it decides whether to log you it or return a fail state of logging.Those confirm email,phone are I assume flag in your database for the user when these are updated when you click of the email link/text code etc.
Your log-in form will use a user/password combination but you can tell spring security to also verify that the flag is ok for the phone/email etc or return a fail state.
instead of just using the default username/password/role authentication provider provided by spring security; you can modify and overwrite the UserDetailsService and the UserDetails class and implements them how you want instead of the default spring security.
ex: I use a custom UserDetailsService to not only look at my username but also verify that its status is active(thus loging-in as a banned or inactive account will return spring log-in error.
via spring-security.xml(wherever you put your spring setting if done via .xml)
<authentication-manager>
<authentication-provider ref="authProvider"/>
</authentication-manager>
<beans:bean id="authProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<beans:property name="userDetailsService" ref="customProvider"/>
</beans:bean>
<beans:bean id="customProvider"
class="com.asura.projectX.authentication.CustomAuthenticationProvider">
</beans:bean>
then your custom log-in that will not only search username but also make sure phone,email etc is ok before trying to log in.example:
public class CustomAuthenticationProvider implements UserDetailsService{
@Inject
private UserService userService;
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
Users user=userService.getUsers(username);
if(user==null) throw new UsernameNotFoundException("Username not found");
return user;
}
}
userService.getUsers(username) will be whatever service/sql you want. In my case it looks into my database for the matching username typed and if the status of account is active or it return error thus spring wont let you log in.The log-in is automatic if everything match(password too obviously and you can even encrypt it/read via spring too).
Upvotes: 0