Store additional information after authentication [Spring] - java

I am developing a web application that has the following requirements:
Allow the user to login
On the server side, the user is authenticated via a 3rd party REST web service.
The REST web service will return a unique token and key, if the authentication is successful.
Any subsequent requests to the REST web service must contain the token received in point 3 (above).
I am using spring-mvc and spring security for the web application.
So, I got a solution working, however I'm new to spring and not sure if the solution is correct.
Can someone please advise if:
Is the solution correctly implemented?
Does the solution impact performance in any way?
Does the solution create any security holes?
Thanks :)
Solution:
I created a MyUser object that will store the additional information received from the REST service.
public class MyUser implements Serializable {
private static final long serialVersionUID = 5047510412099091708L;
private String RestToken;
private String RestKey;
public String getRestToken() {
return RestToken;
}
public void setRestToken(String restToken) {
RestToken = restToken;
}
public String getRestKey() {
return RestKey;
}
public void setRestKey(String restKey) {
RestKey = restKey;
}
}
I then created a MyAuthenticationToken object that extends UsernamePasswordAuthenticationToken. This object will be used in the CustomAuthenticationProvider (point 3 below).
public class MyAuthenticationToken extends UsernamePasswordAuthenticationToken {
private static final long serialVersionUID = 7425814465946838862L;
private MyUser myUser;
public MyAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities, MyUser myUser){
super(principal, credentials, authorities);
this.myUser = myUser;
}
public MyUser getMyUser() {
return myUser;
}
public void setMyUser(MyUser myUser) {
this.myUser = myUser;
}
}
I created a custom authentication provider that will call the REST service for authentication and then store the additional information in the myUser and myAuthenticationToken objects.
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Override
public Authentication authenticate (Authentication authentication) {
MyUser myUser = new MyUser();
MyAuthenticationToken authenticationToken = null;
String name = authentication.getName();
String password = authentication.getCredentials().toString();
//Just an example. This section will connect to a web service in order to authenticate the client
if (name.equals("justh") && password.equals("123456")) {
//Set the Token and Key Received from the REST WebService
myUser.setRestKey("RestKey");
myUser.setRestToken("RestToken");
List<GrantedAuthority> grantedAuths = new ArrayList<GrantedAuthority>();
grantedAuths.add(new SimpleGrantedAuthority("ROLE_USER"));
authenticationToken = new MyAuthenticationToken(name, password, grantedAuths, myUser);
return authenticationToken;
} else {
return null;
}
}
Finally, I can access the data stored in my controller
public ModelAndView adminPage(Authentication authentication) {
MyUser user = null;
//Get the additional data stored
if(authentication instanceof MyAuthenticationToken){
user = ((MyAuthenticationToken)authentication).getMyUser();
}
ModelAndView model = new ModelAndView();
model.addObject("title", "Spring Security Hello World");
model.addObject("message", "This is protected page - Admin Page!" + authentication.getName() + user.getRestKey() + user.getRestToken());
model.setViewName("admin");
return model;
}

Your approach is the right one. You should implement a custom AuthenticationManager and Authentication whenever your requirements exceeds a simple username password authentication flow.
But don't forget to comply with AuthenticationManager's interface contract.
I did something quite similar in my webmailer for authenticating against an smtp/imap server with javax.mail and it works flawlessly.

Related

How do I set up a custom login with Spring Boot?

Question
How do I set up a custom login with Spring Boot? I need to use the same connection method of a legacy app I have.
Things to know before I explain
I have a javafx App that connects to a data base using a php website
as proxy/Login Obviously, this javafx app has the User class already
defined.
The Database is separated by company(group of users), the login process basically retrieves the user object and to which database it should connect.
The javafx application logs in with the web service and retrieves
the database URL for that specific user.
The javafx application uses the database URL to access a database
directly.
Or, to simplify: The user put the login and password and click LOGIN -> The app, goes to the webhost, sends the data to a certain php file and requests the database url and data that comes ENCRYPTED through JASPYR and it also comes through https. Once the data is returned, we decrypt it and login to the database directly.
Im building a SpringBoot application to work with this legacy app.
Where Am I stuck?
I have built a Spring boot maven project and im reading a lot about Spring boot. My first step is to create a login page that behaves as the legacy app.
Currently, Im using a InMemorySecurityConfig as follows:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
#Configuration
public class InMemorySecurityConfig {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password("{noop}123").roles("USER")
.and()
.withUser("admin").password("{noop}123").roles("ADMIN");
}
}
I dont know how to start. I think I have to find away to define the correct User model and then use a class to login that actually allows me to manually login.
Here is the legacy class that I use on my javafx App below.
public JSONObject login(String usuario, String senha) throws MalformedURLException, IOException {
int timeout = 10;
RequestConfig config = RequestConfig.custom()
.setConnectTimeout(timeout * 1000)
.setConnectionRequestTimeout(timeout * 1000)
.setContentCompressionEnabled(true)
.setAuthenticationEnabled(true)
.setSocketTimeout(timeout * 1000)
.build();
CloseableHttpClient httpclient = HttpClientBuilder.create().setDefaultRequestConfig(config).build();
HttpPost httppost = new HttpPost(Settings.LOGIN_URL);
int CONNECTION_TIMEOUT_MS = timeout * 1000; // Timeout in millis.
RequestConfig requestConfig = RequestConfig.custom()
.setConnectionRequestTimeout(CONNECTION_TIMEOUT_MS)
.setConnectTimeout(CONNECTION_TIMEOUT_MS)
.setContentCompressionEnabled(true)
.setAuthenticationEnabled(true)
.setSocketTimeout(CONNECTION_TIMEOUT_MS)
.build();
httppost.setConfig(requestConfig);
// Request parameters and other properties.
ArrayList<NameValuePair> params = new ArrayList<NameValuePair>(2);
params.add(new BasicNameValuePair("usuario", usuario)); // user
params.add(new BasicNameValuePair("senha", senha)); // password
httppost.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
CloseableHttpResponse execute = null;
try {
execute = httpclient.execute(httppost);
} catch (Exception ex) {
try {
httppost.abort();
if (execute != null) {
execute.close();
}
} catch (Exception x) {
ErrorLogger.log(x);
}
throw ex;
}
HttpEntity entity = execute.getEntity();
if (entity != null) {
JSONObject json = JsonTools.readJson(entity.getContent());
return json;
}
return null;
}
#Override
protected Task<JSONObject> createTask() {
return new Task<JSONObject>() {
#Override
protected JSONObject call() throws InterruptedException, IOException {
StringProperty usuario = getUserName();
StringProperty senha = getPassword();
LoginDAO loginDAO = new LoginDAO();
JSONObject login = loginDAO.login(MyTools.encodeToBase64(usuario.get()), MyTools.encodeToBase64(senha.get()));
String db_url = login.getString("db_url");
String db_username = login.getString("db_username");
String db_password = login.getString("db_password");
String nomeEmpresa = login.getString("nomeEmpresa");
int idUsuario = login.getInt("idUsuario");
String nomeDoUsuario = login.getString("nomeUsuario");
int idTunnel = login.getInt("idTunnel");
Settings.setDb_password(db_password);
Settings.setDb_username(db_username);
Settings.setDb_url(db_url);
Settings.setDb_empresa(nomeEmpresa);
//Connect to the data base
HibernateUtil.init();
return login;
}
};
}
How do I start? Any tips on how to start is greatly appreciated.
Im expecting that I have to somehow tell spring boot which User class to use, to tell spring to NOT instantly connect to the database and wait for the database info to be retrieved by the login page, and a way to login using spring.
Spring Boot base on http endpoints like this:
#RestController
#RequestMapping("/api/login")
public class LoginController {
private final LoginService loginService;
#GetMapping
public ObjectYouWantReturn returnDatabaseURL(#RequestBody DtoCredentials dto) {
return loginService.returnDatabaseURL(dto.getUsername, dto.getPassword);
}
}
Your client must send DtoCredentials to https://your.host/api/login. Then server will return secured data to your client. You need to implement mechanism to validation credentials from a user.
This class will load a UserDetailsImpl by username. You can use below class for load data from a database or simply create a List or something with users.
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
#Override
public UserDetailsImpl loadUserByUsername(String username) {
//here you have to create mechanism returning user credentials in UserDetails
}
}
UserDetails is basic DTO, look at the documentation
public class UserDetailsImpl implements UserDetails {
//implement here methods specified by UserDetails
}
And this is Service. In Spring Boot a good practice is separating logic from controllers. In method returnDatabaseURL() we are checking are the credentials correctly. If they are, the server returns data to client, or if not, we can throw ResponseStatusException(HttpStatus.NOT_FOUND) of anything else.
#Service
public class LoginService {
/*Spring Boot should automatically inject UserDetailsServiceImpl because we
annotated it by #Service annotation*/
private final UserDetailsServiceImpl userDetails;
public LoginService(UserDetailsServiceImpl userDetails) {
this.userDetails = userDetails;
}
public ObjectYouWantReturn returnDatabaseURL(String username, String password) {
if (userDetails.loadUserByUsername(username) != null &&
userDetails.loadUserByUsername(username).getPassword().equals(password)) {
return new ObjectYouWantReturn(/*URL to database or anything you want to
return to user*/);
}
}
}
Validating users in the service is not the best way, it is recommended that validate user in filter. But filters is a little bit more advanced issue.
Basic implementation of DtoCredentials:
public class DtoUsernamePassword implements Serializable {
#NotBlank
private final String username;
#NotBlank
private final String password;
public DtoUsernamePassword(#JsonProperty("username") #NotBlank String username,
#JsonProperty("password") #NotBlank String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
}
Needed Jackson dependency:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.11.2</version>
</dependency>
I hope that after read it you will understand Spring Boot a bit more (because as I see you are new in Spring Boot). It's not a completely implementation. If my answer don't satisfying you, treat it as a loose attempt to explain how does Spring Boot work.

Spring Security with JWT token is loading spring UserDetails object on every request

I am learning spring security with JWT token and spring boot. I have implemented it properly and it is working fine. But I have one doubt in how JwtRequestFilter works. I have gone through couple of websites to understand spring security with spring boot and found same thing. So let me go to main doubt.
I am adding JwtRequestFilter file below.
JwtRequestFilter.java
#Component
public class JwtRequestFilter extends OncePerRequestFilter {
#Autowired
private JwtUserDetailsService jwtUserDetailsService;
#Autowired
private JwtTokenUtil jwtTokenUtil;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
final String requestTokenHeader = request.getHeader("Authorization");
String username = null;
String jwtToken = null;
// JWT Token is in the form "Bearer token". Remove Bearer word and get
// only the Token
if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
jwtToken = requestTokenHeader.substring(7);
try {
username = jwtTokenUtil.getUsernameFromToken(jwtToken);
} catch (IllegalArgumentException e) {
System.out.println("Unable to get JWT Token");
} catch (ExpiredJwtException e) {
System.out.println("JWT Token has expired");
}
} else {
logger.warn("JWT Token does not begin with Bearer String");
}
// Once we get the token validate it.
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
// This below line is calling on every request
UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
// if token is valid configure Spring Security to manually set
// authentication
if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
// After setting the Authentication in the context, we specify
// that the current user is authenticated. So it passes the
// Spring Security Configurations successfully.
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
filterChain.doFilter(request, response);
}
}
As highlighed to validate token we have to provide spring UserDetails object and we are getting spring UserDetails object from jwtUserDetailsService. So every request this filter will call then token verification will perform and we have to call jwtUserDetailsService on every request.
My doubt is inside my jwtUserDetailsService I am adding couple of validation and adding user privileges. So on every request below steps are repeated in jwtUserDetailsService.
Get user using username from DB.
Get user role
Get user privileges from DB.
Assign privileges to userDetails.
JwtUserDetailsService.java
#Service("jwtUserDetailsService")
#Transactional
public class JwtUserDetailsService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Autowired
private IUserService service;
#Autowired
private MessageSource messages;
#Autowired
private RoleRepository roleRepository;
#Override
public UserDetails loadUserByUsername(String email)
throws UsernameNotFoundException {
User user = userRepository.findByEmail(email);
if (user == null) {
return new org.springframework.security.core.userdetails.User(
" ", " ", true, true, true, true,
getAuthorities(Arrays.asList(
roleRepository.findByName("ROLE_USER"))));
}
return new org.springframework.security.core.userdetails.User(
user.getEmail(), user.getPassword(), user.isEnabled(), true, true,
true, getAuthorities(user.getRoles()));
}
private Collection<? extends GrantedAuthority> getAuthorities(
Collection<Role> roles) {
return getGrantedAuthorities(getPrivileges(roles));
}
private List<String> getPrivileges(Collection<Role> roles) {
List<String> privileges = new ArrayList<>();
List<Privilege> collection = new ArrayList<>();
for (Role role : roles) {
collection.addAll(role.getPrivileges());
}
for (Privilege item : collection) {
privileges.add(item.getName());
}
return privileges;
}
private List<GrantedAuthority> getGrantedAuthorities(List<String> privileges) {
List<GrantedAuthority> authorities = new ArrayList<>();
for (String privilege : privileges) {
authorities.add(new SimpleGrantedAuthority(privilege));
}
return authorities;
}
}
So on every request these queries are executing. Is there any better way of doing this? Because once I am adding user privileges in spring UserDetails object why we need to do that again on every request. Or those have scope of request only. I have worked on spring mvc and once we add privileges in spring UserDetails object it will be there until I am hitting logout means It will be there in spring security context until we remove it. Will it be same in spring boot? If I am adding role and privileges details once in spring UserDetails object why we need to add it again?
So every request this filter will call then token verification will
perform and we have to call jwtUserDetailsService on every request.
This can't be correct since you have a condition if (SecurityContextHolder.getContext().getAuthentication() == null).
So the first time the token was validated, you query your user details service, fetch all grants and set them to the Security context (you are already doing it: SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);).
Furthermore, with the JWT auth you usually don't even need to access any user details service since all grants ideally should be contained in the token itself. So the only thing you need to do is validating token's signature.
Once an user logs in his authentication is established, so you don't need to do db call again, after login in every request user should be checked for authorization only with the roles being set in the token during authentication, you need to validate the token is not tampered in every request
instead of creating userdetails by loading user detail from db call
UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
you could also encode the user's username and roles inside JWT claims
and create the UserDetails object by parsing those claims from the JWT.

How to authenticate user manually in spring boot?

I have two tables 'user' and 'role'.I want to create a login api (e.g '/login') which will take username and password as a json data. I want to check if given credential is a valid credential and if it is,then I want to set the user as authenticated user so that he/she may have the protected resources. I am new to spring boot framework and I don't know how to do so.I have read the offical documentation but cannot find any resources.Could someone help me on this?
You have number of choices to implement such authentication in Spring.
Case 1:- If you are building REST services then you can implement security in following ways:
i) - you can use Basic-Authentication to authenticate your user.
ii) - you can use OAuth2 to authenticate and authorize your user.
Case 2: If you are building web application
i) - you can use auth token (in case of Single page application SPA)
ii) - you can use session based authentication (traditional login form and all)
I Guess you are in beginner mode so i will recommend you to firstly understand the control flow user authentication in web app via login form. So Let's go through some code.
I'm assuming that you have set a basic spring project and now you are implementing security.
USER - Hibernate entity for your user table;
ROLE - Hibernate entity for your role table
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomAuthProvider customAuthProvider;
#Override
protected void configure(HttpSecurity http) throws Exception {
// everyone is allowed tp view login page
http.authorizeRequests().antMatchers("/login").permitAll().and();
http.authorizeRequests().antMatchers("custom_base_path" + "**").authenticated().and().
formLogin().loginPage("/loginForm).loginProcessingUrl("/loginUser")
.usernameParameter("username").passwordParameter("password")
.defaultSuccessUrl("custom_base_path+ "home", true);
#Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthProvider);
}
//CustomAuthProvider
#Component
public class CustomAuthentiationProvider implements AuthenticationProvider{
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String userid = authentication.getName();
String password = authentication.getCredentials().toString();
Authentication auth = null;
try {
//write your custom logic to match username, password
boolean userExists = your_method_that_checks_username_and_password
if(userExists ){
List<Role> roleList= roleDao.getRoleList(userid);
if (roleList == null || roleList.isEmpty()) {
throw new NoRoleAssignedException("No roles is assigned to "+userid);
}
auth = new UsernamePasswordAuthenticationToken(userid, password,getGrantedAuthorities(roleList));
}
} catch (Exception e) {
log.error("error", e);
}
return auth;
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
public List<GrantedAuthority> getGrantedAuthorities(List<Role> roleList) {
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
for (Role role : roleList) {
authorities.add(new SimpleGrantedAuthority(role.getRoleName());
}
return authorities;
}
}
NOTE: Please consider these codes to understand the logic of authentication. don't consider as perfect code(Not for production env.). You can ping me anytime i'll suggest you more about that.

How to login a user programmatically using Spring-security?

I need to programmatically login users that were authenticated through Facebook API. The reason for that is that there are number of items that are associated to each user (for example shopping cart), therefore once user is authenticated using Facebook API, I need to log the user in using spring security as well to be able to access his/her shopping cart.
Based on my research, there are many methods to implement it but I could not deploy any of them as I am sending log-in request from my code, also another problem is that some people created user object but they did not explain how to create it.
Those who created a user object but did not explain how.
From first example:this answer
Authentication auth =
new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
From second example: this one
34.User details = new User(username);
35.token.setDetails(details);
From third example: this one
Authentication authentication = new UsernamePasswordAuthenticationToken(user, null,
AuthorityUtils.createAuthorityList("ROLE_USER"));
Another example is here, it does not help because I need to log-in user from my own code not from browser; therefore I do not know how to populate HttpServletRequest object.
protected void automatedLogin(String username, String password, HttpServletRequest request) {
MyCode
...
if(isAuthenticatedByFB())
{
login(username);
return "success";
}
else{
return "failed";
}
Unfortunately it seems there is no "complete" support of programmatic login in Spring security. Here is how I've done it successfully:
#Autowired AuthenticationSuccessHandler successHandler;
#Autowired AuthenticationManager authenticationManager;
#Autowired AuthenticationFailureHandler failureHandler;
public void login(HttpServletRequest request, HttpServletResponse response, String username, String password) {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
token.setDetails(new WebAuthenticationDetails(request));//if request is needed during authentication
Authentication auth;
try {
auth = authenticationManager.authenticate(token);
} catch (AuthenticationException e) {
//if failureHandler exists
try {
failureHandler.onAuthenticationFailure(request, response, e);
} catch (IOException | ServletException se) {
//ignore
}
throw e;
}
SecurityContext securityContext = SecurityContextHolder.getContext();
securityContext.setAuthentication(auth);
successHandler.onAuthenticationSuccess(request, response, auth);//if successHandler exists
//if user has a http session you need to save context in session for subsequent requests
HttpSession session = request.getSession(true);
session.setAttribute("SPRING_SECURITY_CONTEXT", securityContext);
}
UPDATE Essentially the same is done by Spring's RememberMeAuthenticationFilter.doFilter()
This code is from Grails' Spring Security Core -Plugin, which is released under the Apache 2.0 license. I've added the imports just to point out what the types are exactly. The original author is Burt Beckwith.
import org.springframework.security.core.userdetails.UserCache;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
...
public static void reauthenticate(final String username, final String password) {
UserDetailsService userDetailsService = getBean("userDetailsService");
UserCache userCache = getBean("userCache");
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(
userDetails, password == null ? userDetails.getPassword() : password, userDetails.getAuthorities()));
userCache.removeUserFromCache(username);
}
The getBean-method merely provides the bean from application context.

SpringSecurity UserDetailsService get password

I'm creating authentication service in Spring.
I'm using UserDetailsService to get form variables, but i found that loadUserByUsername has only one variable - userName.
How to get password ?
public class userAuthentication implements UserDetailsService{
private #Autowired
ASPWebServicesUtils aspWebServicesUtils;
#Override
public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {
//how to get password ?
User user = new User("test", "test", true, true, true, true, getAuthorities(true));
return user;
}
private List<GrantedAuthority> getAuthorities(boolean isAdmin){
List<GrantedAuthority> authorityList = new ArrayList<GrantedAuthority>(2);
authorityList.add(new SimpleGrantedAuthority("USER_ROLE"));
if(isAdmin){
authorityList.add(new SimpleGrantedAuthority("ADMIN_ROLE"));
}
return authorityList;
}
//...
}
Thanks
If you look at the User object, the second parameter in the constructor is the password.
The UserDetailsService is used to load the user from a back-end structure like database. The loadUserByUsername method is called when a user tries to login with a username and password, then it is the responsibility of the service to load the user definition and return it to the security framework. The required details includes data like username, password, accountNonExpired, credentialsNonExpired, accountNonLocked and authorities.
Once the spring security receives the user object, it will validate the user against the password entered by the user and other data like user account status (accountNonExpired, credentialsNonExpired etc)
Some of the standard (out-of-the-box) mechanisms to retrieve the user information and provide authentication information are:
inMemoryAuthentication
jdbcAuthentication
ldapAuthentication
userDetailsService
If the above does not suit your purpose and you need to have a custom solution, you can create and configure a new authentication provider like so:
Security Configuration:
#Configuration
#EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
#Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(new CustomAuthenticationProvider());
}
....
}
Authentication Provider:
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
String name = authentication.getName();
// You can get the password here
String password = authentication.getCredentials().toString();
// Your custom authentication logic here
if (name.equals("admin") && password.equals("pwd")) {
Authentication auth = new UsernamePasswordAuthenticationToken(name,
password);
return auth;
}
return null;
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
I believe a UserDetailsService is supposed to be used to acquire a UserDetails object from some back end storage, database, flat file, etc. Once you have that UserDetails, spring security (or you) have to compare it to the username (or other principals) and password (the credentials) provided by the user in order to authenticate that user.
I don't think you are using it the way it is intended.
Get password in UserDetailsService implementation by request.getParameter("password"):
public class MyUserDetailsService implements UserDetailsService {
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String password = request.getParameter("password"); // get from request parameter
......
}
}
RequestContextHolder is base on ThreadLocal.
If your project is base on Spring Framework (not Spring Boot), add RequestContextListener to web.xml
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
XML Implementation:
<authentication-manager alias="loginAuthenticationManager">
<authentication-provider ref="loginAuthenticationProvider" />
</authentication-manager>
<!-- Bean implementing AuthenticationProvider of Spring Security -->
<beans:bean id="loginAuthenticationProvider" class="com.config.LoginAuthenticationProvider">
</beans:bean>
AuthenticationProvider:
public class LoginAuthenticationProvider implements AuthenticationProvider {
#Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
String name = authentication.getName();
// You can get the password here
String password = authentication.getCredentials().toString();
// Your custom authentication logic here
if (name.equals("admin") && password.equals("pwd")) {
List<GrantedAuthority> grantedAuths = new ArrayList<>();
grantedAuths.add(new SimpleGrantedAuthority("ROLE_USER"));
return new UsernamePasswordAuthenticationToken(name, password, grantedAuths);
}
return null;
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
the loadUserByUsername(String name) is a method defined on an interface (userServicedetails I think), which your service implements. You have to write the implementation.
Just as you have to write the implementation for getPassword() or similar ... spring does not provide that. I imagine the password is stored in your user object, but you wrote that ... did you create a getPassword() method ?

Categories