I'm developing gwt application which I want to secure using spring-security. I have users data in database and UserService is responsible for getting particular User. I have followed this tutorial
AuthenticationProvider:
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Autowired UserService userService;
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = (String) authentication.getPrincipal();
String password = (String) authentication.getCredentials();
User user = userService.findByUserName(username);
if (user == null) {
throw new UsernameNotFoundException("User not found");
}
String storedPass = user.getPassword();
if (!storedPass.equals(password)) {
throw new BadCredentialsException("Invalid password");
}
Authentication customAuthentication = new CustomUserAuthentication(user, authentication);
customAuthentication.setAuthenticated(true);
return customAuthentication;
}
#Override
public boolean supports(Class<?> authentication) {
return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
}
}
CustomAuthentication
public class CustomUserAuthentication implements Authentication {
private static final long serialVersionUID = -3091441742758356129L;
private boolean authenticated;
private final GrantedAuthority grantedAuthority;
private final Authentication authentication;
private final User user;
public CustomUserAuthentication(User user, Authentication authentication) {
this.grantedAuthority = new SimpleGrantedAuthority(user.getRole().name());
this.authentication = authentication;
this.user = user;
}
#Override
public Collection<GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(grantedAuthority);
return authorities;
}
#Override
public Object getCredentials() {
return authentication.getCredentials();
}
#Override
public Object getDetails() {
return authentication.getDetails();
}
#Override
public Object getPrincipal() {
return user;
}
#Override
public boolean isAuthenticated() {
return authenticated;
}
#Override
public void setAuthenticated(boolean authenticated) throws IllegalArgumentException {
this.authenticated = authenticated;
}
#Override
public String getName() {
return user.getUsername();
}
}
security context:
<s:http auto-config="true" create-session="always" >
<s:intercept-url pattern="/index.html" access="ROLE_USER" />
<s:logout logout-success-url="/login.html"/>
<s:form-login login-page="/login.html" default-target-url="/index.html" authentication-failure-url="/login.html" />
</s:http>
<s:authentication-manager alias="authenticationManager">
<s:authentication-provider ref="customAuthenticationProvider" />
</s:authentication-manager>
<bean id="customAuthenticationProvider" class="com.example.server.security.CustomAuthenticationProvider" />
Everything works fine, spring intercept call to index.html i need to log and it redirects me back to index.html. The problem is when i log out and then go to index.html once again I just simply get access to it. I figured out that:
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
System.out.println("Logged as: " + auth.getName());
prints anonymousUser after logout. This code prints my user name when I log in again so I suppose that there is something wrong with intercepting anonymous user. Does anyone knows how to intercept anonymous user?
Instead of:
<s:intercept-url pattern="/**" access="ROLE_USER" />
You can use:
<s:intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY,ROLE_USER" />
That should make Spring Security deny access to the anonymous user. Of course, that implies you also need to add one of these:
<s:intercept-url pattern="/url_that_should_be_accessible_to_anonymous_user" access="IS_AUTHENTICATED_ANONYMOUSLY" />
For every pattern that anonymous users should be able to access. Typically, login pages, error pages, static resources (images, PDF, etc).
Related
I want to use two types of users: common user and admin. Now I already have an infrastructure where admins and users are two completely different types: users have many things related only to them(controllers, tables, services etc.), same for admins. Therefore, they are different entities and different tables in the DB, and I don't want to combine them, because they are different. But now only users can log in using Spring Security OAuth2, but admins not principals and they can't log in. Note that I use my own authorization and resource servers.
So, I want to allow Spring Security to authenticate both users and admins. I also want to use two different login endpoints and two different entities and tables for users and admins.
How can this be done or what should I do instead?
UPD:
I think I should create 2 OAuth clients with 2 different grant_types in oauth_client_details and 2 AbstractTokenGranters for users and for admins.
I already have a custom AbstractTokenGranter for users which authenticate users like this:
//getOAuth2Authentication()
User user = userService.getUserByPhone(username);
if(user == null)
throw new BadCredentialsException("Bad credentials");
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(Long.toString(user.getId()), password)
);
//I use Long.toString(user.getId()) because some users use FB instead of the phone,
//so I have one more `AbstractTokenGranter` for social networks,
//I don't mention about it in this post, so don't be confused
As I understand, AuthenticationManager calls UserDetailsService, which looks like this now:
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserDetails user = userRepository.findById(Long.parseLong(username)).orElseThrow(
() -> new UsernameNotFoundException("User not found with id : " + id)
);
return user;
}
But if I create one more AbstractTokenGranter for admins, then the current UserDetailsService will not know whose id it received - admin id or user id.
As a solution, I think I need to create one more UserDetailsService for admins. But how can I use multiple UserDetailsService? Also, maybe I should use a completely different scheme?
<security:http pattern="/oauth/token" use-expressions="true" create-session="stateless"
authentication-manager-ref="clientAuthenticationManager"
xmlns="http://www.springframework.org/schema/security">
<security:intercept-url pattern="/**" method="GET" access="ROLE_DENY"/>
<security:intercept-url pattern="/**" method="PUT" access="ROLE_DENY"/>
<security:intercept-url pattern="/**" method="DELETE" access="ROLE_DENY"/>
<security:intercept-url pattern="/oauth/token" access="permitAll"/>
<security:anonymous enabled="false"/>
<security:http-basic entry-point-ref="clientAuthenticationEntryPoint"/>
<!-- include this only if you need to authenticate clients via request
parameters -->
<security:custom-filter ref="contentTypeFilter" before="BASIC_AUTH_FILTER"/>
<security:custom-filter ref="clientCredentialsTokenEndpointFilter"
after="BASIC_AUTH_FILTER"/>
<security:access-denied-handler ref="oauthAccessDeniedHandler"/>
<security:csrf disabled="true"/>
</security:http>
You can define custom clientDetailService and override loadUserByUserName method.
It is up to you whether you can query different tables and authorizations also you can change the structure. That is what i can say without more description
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
ClientDetails clientDetails;
try {
clientDetails = clientDetailsService.loadClientByClientId(username);
} catch (NoSuchClientException e) {
throw new UsernameNotFoundException(e.getMessage(), e);
}
String clientSecret = clientDetails.getClientSecret();
if (clientSecret== null || clientSecret.trim().length()==0) {
clientSecret = emptyPassword;
}
return new User(username, clientSecret, clientDetails.getAuthorities());
}
This part can be modified to change structure :> authentication-manager-ref="clientAuthenticationManager"
If you are not using xml based, you can check annotation base link :
https://www.baeldung.com/spring-security-authentication-with-a-database
1.Create new OAuth2 client in the oauth_client_details table with custom_grant in the authorized_grant_types.
2.Create:
public class CustomTokenGranter extends AbstractTokenGranter {
//...
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
Map<String, String> params = tokenRequest.getRequestParameters();
String username = params.getOrDefault("username", null);
String password = params.getOrDefault("password", null);
if(username == null || password == null)
throw new BadCredentialsException("Bad credentials");
CustomAuthenticationToken token = new CustomAuthenticationToken(username, password);
Authentication authentication = authenticationManager.authenticate(token);
}
}
3.Add this granter in AuthorizationServerConfigurerAdapter:
private TokenGranter tokenGranter(final AuthorizationServerEndpointsConfigurer endpoints) {
List<TokenGranter> granters = new ArrayList<TokenGranter>(Arrays.asList(endpoints.getTokenGranter()));
granters.add(new CustomGrantTokenGranter(endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory(), "custom_grant"));
return new CompositeTokenGranter(granters);
}
Now CustomGrantTokenGranter will receive all authorization request with custom_grant grant type.
4.Create CustomAuthenticationToken extends UsernamePasswordAuthenticationToken
5.Create:
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Autowired
private PasswordEncoder adminPasswordEncoder;
#Autowired
private UserDetailsService adminDetailsService;
#Override
public Authentication authenticate(Authentication auth) throws AuthenticationException {
String username = auth.getName();
String password = auth.getCredentials().toString();
UserDetails adminDetails = adminDetailsService.loadUserByUsername(username);
//adminDetailsService.loadUserByUsername(username) returns Admin inside UserDetails
if (adminPasswordEncoder.matches(password, adminDetails.getPassword()))
return new UsernamePasswordAuthenticationToken(adminDetails, password, adminDetails.getAuthorities());
else
throw new BadCredentialsException("Bad credentials");
}
#Override
public boolean supports(Class<?> auth) {
return auth.equals(CustomAuthenticationToken.class);
}
}
Here you can use UserDetailsService different from other providers
6.Add CustomAuthenticationProvider in WebSecurityConfigurerAdapter
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomAuthenticationProvider customAuthenticationProvider;
#Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) {
authenticationManagerBuilder.authenticationProvider(customAuthenticationProvider);
}
//...
}
Summary: with this scheme you can use as many user types as you want. If Admin implements UserDetails, then you can easily use it on the server as a Principal.
I want to get the user role after logout button is clicked.
if the role is admin i have to return /login.jsp in /logout
if the role is user i have to return /index.jsp in /logout
Thanks in advance
my controller.java:
#RequestMapping(value="/logout",method=RequestMethod.GET)
public String logout(HttpServletRequest request,ModelMap model)
{
model.addAttribute("userForms",userService.getActiveUserList());
model.addAttribute("Success",true);
return "/login";
}
UserService.java
public List<UserForm> getActiveUserList()
{
List<UserForm> userForms = new ArrayList<UserForm>();
List<User> users = new ArrayList<User>();
users = userDAO.getActiveList();
for (User user : users) {
String crmDomainLink=crmProperties.getProperty("CRMAppDomain");
UserForm userForm = new UserForm(
user.getUserId(),user.getName(), user.getCode(),
CRMConstants.convertUSAFormatWithTime(user.getCreatedDateTime()),
user.getIsEnabled(), null);
userForms.add(userForm);
}
return userForms;
}
MyDAO.java
public List<User> getActiveList() {
return this.sessionFactory.getCurrentSession().createCriteria(User.class).add(Restrictions.and(Restrictions.eq("isEnabled", 1),Restrictions.ne("userId", 1))).list();
}
You should implement a custom LogoutSuccessHandler. Something like:
#Component
public class CustomLogoutSuccessHandler implements LogoutSuccessHandler {
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
if (AuthorityUtils.authorityListToSet(authentication.getAuthorities()).contains("ROLE_ADMIN")) {
response.sendRedirect("/login.jsp");
} else {
response.sendRedirect("/index.jsp");
}
}
}
Add it to security config, if XML:
<logout success-handler-ref="customLogoutSuccessHandler" />
You can get Authentication object in controller by following
#RequestMapping(value="/logout", method = RequestMethod.GET)
public String logout(ModelMap model, Authentication authentication) {
}
Then you can get the roles of logged in user by calling the following method
authentication.getAuthorities();
I am working on a Spring-MVC application in which I am using Spring-Security for authentication. Due to excessive usage of getting the currently authenticated user mechanism, Profiler shows it as an 'Allocation Hotspot' and nearly 9.5kb memory is consumed for a single user. Is there any way to optimize this infrastructure.
PersonServiceImpl :
#Override
public Person getCurrentlyAuthenticatedUser() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
return null;
} else {
return personDAO.findPersonByUsername(authentication.getName());
}
}
If I somehow always can push the user in some cache from where it is retrieved after first retrieving, atleast that should improve the performance, but I have no idea how to do that. Any suggestions are welcome. Thanks.
You can use some variants. But also you can try to ue only Spring:
1. Extends org.springframework.security.core.userdetails.User and add into your UserEntity (Person)
public class User extends org.springframework.security.core.userdetails.User {
private Person sysUser;
public User(Person sysUser) {
super(sysUser.getLogin(), sysUser.getPassword(), new ArrayList<>());
this.sysUser = sysUser;
}
public Person getYourUser(){
return sysUser;
}
}
Implements UserDetailsService using your User Entity and extended org.springframework.security.core.userdetails.User
#Service(value = "userService")
public class UserService implements UserDetailsService {
#Autowired
SecurityDAO securityDAO;
#Override
public UserDetails loadUserByUsername(String login) throws UsernameNotFoundException {
User user = null;
try {
Person su = securityDAO.getUserOnlyByLogin(login);
if (su != null)
user = new User(Person);
} catch (Exception e) {
e.printStackTrace();
}
return user;
}
}
Configure your Spring (xml or Annotation):
<beans:bean id="userService" class="your.pack.UserService"/>
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="userService"/>
</authentication-manager>
use your method
#Override
public Person getCurrentlyAuthenticatedUser() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
return null;
} else { User user = (User) authentication.getPrincipal();
return user.getYourUser();
}
}
I would like to enable multi sign-in for my spring security application i.e. For example, if I have two email addresses, I would want to allow the user to sign in with multiple email addresses and the user can shift from one email account to the other, just as Gmail Multiple Sign-in. How could I do that with Spring security?
There seems to be only one Principal instead of list of principals in the Spring security. Could I achieve it?
SecurityContextHolder.getContext().getAuthentication().getPrincipal();
Thanks in advance. Hope you will reply as soon as possible.
To some extent Spring Security supports user switch. It is more like a su under Linux.
Nevertheless, you can reuse some code from SwitchUserFilter to create your own user switch.
Primarily, you need to create
Custom Spring Security UserDetails which holds a list of usernames a switch is allowed to
Custom UserDetailsService which populates your custom UserDetails
Custom UserSwitchFilter based on Spring's SwitchUserFilter
Custom UserDetails and UserDetailsService are just examples here and may differ from your own implementation. The idea is to hold a list of usernames in UserDetails for later processing in custom UserSwitchFilter.
CustomUserDetails:
public class CustomUserDetails extends User {
private final Set<String> linkedAccounts;
public CustomUserDetails(String username, String password, Set<String> linkedAccounts, Collection<? extends GrantedAuthority> authorities) {
super(username, password, authorities);
this.linkedAccounts = linkedAccounts;
}
public Set<String> getLinkedAccounts() {
return linkedAccounts;
}
}
CustomUserDetailsService:
public class CustomUserDetailsService implements UserDetailsService {
private UserDao userDao = ...;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
BackendUser user = userDao.findUserByUsername(username);
return new CustomUserDetails(user.getHane(), ......);
}
}
Main differences to Spring Security UserSwitchFilter:
Added method checkSwitchAllowed checks if a switch to that specific user from current authenticated user is allowed
switch is based on query paramater and not url for better user experience (see requiresSwitchUser). hence no need for switchUserUrl and targetUrl
Custom UserSwitchFilter ha no notion of a exitUserUrl. Hence no need for exitUserUrl
createSwitchUserToken doesn't modify user authorities
CustomSwitchUserFilter:
public class CustomSwitchUserFilter extends GenericFilterBean implements ApplicationEventPublisherAware, MessageSourceAware {
public static final String SPRING_SECURITY_SWITCH_USERNAME_KEY = "j_switch_username";
private ApplicationEventPublisher eventPublisher;
private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
private String switchFailureUrl;
private String usernameParameter = SPRING_SECURITY_SWITCH_USERNAME_KEY;
private UserDetailsService userDetailsService;
private UserDetailsChecker userDetailsChecker = new AccountStatusUserDetailsChecker();
private AuthenticationFailureHandler failureHandler;
#Override
public void afterPropertiesSet() {
Assert.notNull(userDetailsService, "userDetailsService must be specified");
if (failureHandler == null) {
failureHandler = switchFailureUrl == null ? new SimpleUrlAuthenticationFailureHandler() :
new SimpleUrlAuthenticationFailureHandler(switchFailureUrl);
} else {
Assert.isNull(switchFailureUrl, "You cannot set both a switchFailureUrl and a failureHandler");
}
}
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
// check for switch or exit request
if (requiresSwitchUser(request)) {
// if set, attempt switch and store original
try {
Authentication targetUser = attemptSwitchUser(request);
// update the current context to the new target user
SecurityContextHolder.getContext().setAuthentication(targetUser);
} catch (AuthenticationException e) {
logger.debug("Switch User failed", e);
failureHandler.onAuthenticationFailure(request, response, e);
return;
}
}
chain.doFilter(request, response);
}
protected Authentication attemptSwitchUser(HttpServletRequest request) throws AuthenticationException {
UsernamePasswordAuthenticationToken targetUserRequest;
String username = request.getParameter(usernameParameter);
if (username == null) {
username = "";
}
if (logger.isDebugEnabled()) {
logger.debug("Attempt to switch to user [" + username + "]");
}
UserDetails targetUser = userDetailsService.loadUserByUsername(username);
userDetailsChecker.check(targetUser);
checkSwitchAllowed(targetUser);
// OK, create the switch user token
targetUserRequest = createSwitchUserToken(request, targetUser);
if (logger.isDebugEnabled()) {
logger.debug("Switch User Token [" + targetUserRequest + "]");
}
// publish event
if (this.eventPublisher != null) {
eventPublisher.publishEvent(new AuthenticationSwitchUserEvent(SecurityContextHolder.getContext().getAuthentication(), targetUser));
}
return targetUserRequest;
}
private void checkSwitchAllowed(UserDetails targetUser) {
CustomUserDetails details = (CustomUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
String targetUsername = targetUser.getUsername();
//target username has to be in linked accounts otherwise this is an unauthorized switch
if(!details.getLinkedAccounts().contains(targetUsername)) {
throw new InsufficientAuthenticationException("user switch not allowed");
}
}
private UsernamePasswordAuthenticationToken createSwitchUserToken(HttpServletRequest request, UserDetails targetUser) {
UsernamePasswordAuthenticationToken targetUserRequest;
// get the original authorities
Collection<? extends GrantedAuthority> orig = targetUser.getAuthorities();
// add the new switch user authority
List<GrantedAuthority> newAuths = new ArrayList<GrantedAuthority>(orig);
// create the new authentication token
targetUserRequest = new UsernamePasswordAuthenticationToken(targetUser, targetUser.getPassword(), newAuths);
// set details
targetUserRequest.setDetails(authenticationDetailsSource.buildDetails(request));
return targetUserRequest;
}
protected boolean requiresSwitchUser(HttpServletRequest request) {
Map<String, String[]> parameterMap = request.getParameterMap();
return parameterMap.containsKey(usernameParameter);
}
public void setApplicationEventPublisher(ApplicationEventPublisher eventPublisher)
throws BeansException {
this.eventPublisher = eventPublisher;
}
public void setAuthenticationDetailsSource(AuthenticationDetailsSource<HttpServletRequest,?> authenticationDetailsSource) {
Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required");
this.authenticationDetailsSource = authenticationDetailsSource;
}
public void setMessageSource(MessageSource messageSource) {
Assert.notNull(messageSource, "messageSource cannot be null");
this.messages = new MessageSourceAccessor(messageSource);
}
public void setUserDetailsService(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
public void setSwitchFailureUrl(String switchFailureUrl) {
Assert.isTrue(StringUtils.hasText(usernameParameter) && UrlUtils.isValidRedirectUrl(switchFailureUrl),
"switchFailureUrl cannot be empty and must be a valid redirect URL");
this.switchFailureUrl = switchFailureUrl;
}
public void setFailureHandler(AuthenticationFailureHandler failureHandler) {
Assert.notNull(failureHandler, "failureHandler cannot be null");
this.failureHandler = failureHandler;
}
public void setUserDetailsChecker(UserDetailsChecker userDetailsChecker) {
this.userDetailsChecker = userDetailsChecker;
}
public void setUsernameParameter(String usernameParameter) {
this.usernameParameter = usernameParameter;
}
}
Add CustomSwitchUserFilter to your security filter chain. It has to be placed after FILTER_SECURITY_INTERCEPTOR.
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider user-service-ref="userDetailsService"/>
</security:authentication-manager>
<security:http use-expressions="true">
<security:intercept-url pattern="/**" access="isFullyAuthenticated()" />
<security:form-login login-page="/login.do" />
<security:logout logout-success-url="/login.do" />
<security:custom-filter ref="switchUserProcessingFilter" after="FILTER_SECURITY_INTERCEPTOR" />
</security:http>
<bean id="switchUserProcessingFilter" class="security.CustomSwitchUserFilter">
<property name="userDetailsService" ref="userDetailsService" />
</bean>
You can find a working example here.
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 ?