Spring boot OAUTH2 cannot refresh_token - UserDetailService is required - java

I'm using Spring Boot version 1.4.3.RELEASE and trying to set up my Authorization Server.
I have no problem when creating the token or accessing the resources when the access_token is valid, but when it expires and I try to refresh it, i'm getting an error
{
"error": "server_error",
"error_description": "UserDetailsService is required."
}
This is the request i make to refresh the access_token
http://localhost:11134/oauth/token?grant_type=refresh_token&refresh_token=0d17dfee-1185-480d-af30-ba0f1e47831c&scope=read
refresh_token is correct
This is my AuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter implements EnvironmentAware {
private static final String ENV_OAUTH = "security.oauth2.client.";
private static final String PROP_CLIENTID = "client-id";
private static final String PROP_SECRET = "client-secret";
private static final String PROP_TOKEN_VALIDITY_SECONDS = "access-token-validity-seconds";
private static final String PROP_REFRESH_TOKEN_VALIDITY_SECONDS = "refresh-token-validity-seconds";
private RelaxedPropertyResolver propertyResolver;
#Autowired
private DataSource dataSource;
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
#Autowired
private CustomUserDetailsService userDetailsService;
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints
.tokenStore(tokenStore())
.authenticationManager(authenticationManager).userDetailsService(userDetailsService);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient(propertyResolver.getProperty(PROP_CLIENTID))
.scopes("read", "write", "trust")
.authorities(Authorities.ROLE_ADMIN.name(), Authorities.ROLE_USER.name())
.authorizedGrantTypes("password", "refresh_token")
.secret(propertyResolver.getProperty(PROP_SECRET))
.accessTokenValiditySeconds(propertyResolver.getProperty(PROP_TOKEN_VALIDITY_SECONDS, Integer.class))
.refreshTokenValiditySeconds(propertyResolver.getProperty(PROP_REFRESH_TOKEN_VALIDITY_SECONDS, Integer.class));
}
public void setEnvironment(Environment environment) {
this.propertyResolver = new RelaxedPropertyResolver(environment, ENV_OAUTH);
}
}
Then this is WebSecurityConfiguration
#Configuration
#EnableWebSecurity
// #EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Bean
public PasswordEncoder passwordEncoder() {
return new StandardPasswordEncoder();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
if it's interesting, this is CustomUserDetailService
#Component("userDetailsService")
public class CustomUserDetailsService implements org.springframework.security.core.userdetails.UserDetailsService {
private final Logger log = LoggerFactory.getLogger(UserDetailsService.class);
#Autowired
private UserRepository userRepository;
#Override
#Transactional
public UserDetails loadUserByUsername(final String login) {
log.debug("Authenticating {}", login);
String lowercaseLogin = login.toLowerCase();
User userFromDatabase;
userFromDatabase = userRepository.findByUsernameCaseInsensitive(lowercaseLogin);
Collection<GrantedAuthority> grantedAuthorities = new ArrayList<>();
for (Authority authority : userFromDatabase.getAuthorities()) {
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(authority.getName());
grantedAuthorities.add(grantedAuthority);
}
return new org.springframework.security.core.userdetails.User(userFromDatabase.getUsername(), userFromDatabase.getPassword(), grantedAuthorities);
}
}

Related

403 Forbidden page Spring Security

I can't seem to find the 403 error problem, when I send a JSON request to register in Postman I get the 403 Forbidden error with no message.
Postman Screenshot
SecureConfig.Java
This is the configuration file.
#EnableWebSecurity
#AllArgsConstructor
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final UserDetailsService userDetailsService;
#Bean(BeanIds.AUTHENTICATION_MANAGER)
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
public void configure(HttpSecurity httpSecurity) throws Exception{
httpSecurity.csrf()
.disable()
.authorizeRequests()
.antMatchers("/api/auth/**")
.permitAll()
.anyRequest()
.authenticated();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
#Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
AuthConroller
This is the controller of the project to authenticate, when I sign up the new user was added to the database successfully, it sends afterwards a mail verification to the fake mail using Mailtrap " https://mailtrap.io " . But when I request to receive the authentication token and the username I receive the error.
#RestController
#AllArgsConstructor
#RequestMapping(value = "/api/auth", consumes = MediaType.APPLICATION_JSON_VALUE)
public class AuthController {
private final AuthService authService;
#PostMapping("/signup")
public ResponseEntity<String> signup(#RequestBody RegisterRequest registerRequest ) {
authService.signup(registerRequest);
return new ResponseEntity<>("User's Registration was successful", HttpStatus.OK);
}
#GetMapping("accountVerification/{token}")
public ResponseEntity<String> verifyAccount(#PathVariable String token){
authService.verifyAccount(token);
return new ResponseEntity<>("Account created successfully", HttpStatus.OK);
}
#PostMapping("/")
public String login(#RequestBody LoginRequest loginRequest){
return authService.login(loginRequest);
}
}
AuthService
This is the autentication service.
#Service
#AllArgsConstructor
public class AuthService {
private final PasswordEncoder passwordEncoder;
private final UserRepository userRepository;
private final VerificationTokenRepository verificationTokenRepository;
private final MailService mailService;
private final AuthenticationManager authenticationManager;
private final JwtProvider jwtProvider;
public void signup(RegisterRequest registerRequest){
User user = new User();
user.setUsername(registerRequest.getUsername());
user.setEmail(registerRequest.getEmail());
user.setPassword(passwordEncoder.encode(registerRequest.getPassword()));
user.setEnabled(false);
user.setCreated(Instant.now());
userRepository.save(user);
String token = generetedVerificationToken(user);
mailService.sendMail(new NotificationEmail("Please " +
"Activate Your Account", user.getEmail(), "Thanks " +
"for signing up. Click on the link below to go home: " +
"http://localhost:8080/api/auth/accountVerification/" + token));
}
private String generetedVerificationToken(User user){
String token = UUID.randomUUID().toString();
VerificationToken verificationToken = new VerificationToken();
verificationToken.setToken(token);
verificationToken.setUser(user);
verificationTokenRepository.save(verificationToken);
return token;
}
public void verifyAccount(String token) {
Optional<VerificationToken> verificationToken = verificationTokenRepository.findByToken(token);
fetchUserAndEnable(verificationToken.orElseThrow(() -> new SpringRedditException("Invalid Token")));
}
private void fetchUserAndEnable(VerificationToken verificationToken){
String username = verificationToken.getUser().getUsername();
Optional<org.springframework.security.core.userdetails.User> v = userRepository.findByUsername(username);
UserDetails user = userRepository.findByUsername(username).orElseThrow(() -> new SpringRedditException("User not found with name - " + username));
User s = (User) user;
s.setEnabled(true);
userRepository.save(s);
}
public String encodePassword(String password) { return passwordEncoder.encode(password);}
public String login(LoginRequest loginRequest){
Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(
loginRequest.getUsername(),
loginRequest.getPassword()));
SecurityContextHolder.getContext().setAuthentication(authentication);
return jwtProvider.generatedToken(authentication);
}
}
JwtProvider
This class is to generate the tokens for each user. I didn't use the JKS I've found in the tutoriel, because it provides an error.
#Service
public class JwtProvider {
// private KeyStore keyStore;
private Key key;
#PostConstruct
public void init() {
key = Keys.secretKeyFor(SignatureAlgorithm.HS512);
}
public String generatedToken(Authentication authentication){
org.springframework.security.core.userdetails.User principal =
(org.springframework.security.core.userdetails.User) authentication.getPrincipal();
return Jwts.builder()
.setSubject(principal.getUsername())
.signWith(key)
.compact();
}
}
UserDetailsServiceImpl
Here goes the implementation that gives the user authority to connect :
#Service
#AllArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {
private final UserRepository userRepository;
#Override
#Transactional
public UserDetails loadUserByUsername(String username) {
Optional<User> userOptional = userRepository.findByUsername(username);
User user = userOptional.orElseThrow(() -> new UsernameNotFoundException("No such user" +
"with username : " + username));
return new org.springframework.security.core.userdetails.User(user.getUsername(),
user.getPassword(), user.isEnabled(), true,
true, true,
getAuthorities("USER"));
}
private Collection<? extends GrantedAuthority> getAuthorities(String role){
return singletonList(new SimpleGrantedAuthority(role));
}
}
and last
AutheticationResponse
In the DTO file we have this class in order to receive the authenticatonToken and the username in postman as a JSON format.
#Data
#AllArgsConstructor
#NoArgsConstructor
public class AuthenticationResponse {
private String authenticationToken;
private String username;
}
After adding logging.level.org.springframework.security=DEBUG to application.properties file, I managed to see the reason this error appears in the below image.
Debug results Screenshot

Spring security | Why do I get bad credentials when they are valid?

So I am working on a Spring boot project and security is one of the things I want to have in this project.
I am having this problem where my code always goes to the BadCredentialsException, but I thought my credentials are correct.
My AuthenticationController:
#RestController
#CrossOrigin
public class JwtAuthenticationController {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private JwtTokenUtil jwtTokenUtil;
#Autowired
private JwtUserDetailsService userDetailsService;
#RequestMapping(value = "/authenticate", method = RequestMethod.POST)
public ResponseEntity<?> createAuthenticationToken(#RequestBody JwtRequest authenticationRequest) throws Exception {
authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword());
final UserDetails userDetails = userDetailsService
.loadUserByUsername(authenticationRequest.getUsername());
final String token = jwtTokenUtil.generateToken(userDetails);
return ResponseEntity.ok(new JwtResponse(token));
}
private void authenticate(String username, String password) throws Exception {
try {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
} catch (DisabledException e) {
throw new Exception("USER_DISABLED", e);
} catch (BadCredentialsException e) {
throw new Exception("INVALID_CREDENTIALS", e);
}
}
#RequestMapping(value = "/register", method = RequestMethod.POST)
public ResponseEntity<?> saveUser(#RequestBody UserDTO user) throws Exception {
return ResponseEntity.ok(userDetailsService.save(user));
}
}
My UserDetailsService:
#Service
public class JwtUserDetailsService implements UserDetailsService {
#Autowired
private UserDao userDao;
#Autowired
private PasswordEncoder bcryptEncoder;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
DAOUser user = userDao.findByUsername(username);
if (user == null){
throw new UsernameNotFoundException("User not found with username: " + username);
}
return new User(user.getUsername(), user.getPassword(), new ArrayList<>());
}
public DAOUser save(UserDTO user) {
DAOUser newUser = new DAOUser();
newUser.setUsername(user.getUsername());
newUser.setPassword(bcryptEncoder.bCryptPasswordEncoder().encode(user.getPassword()));
return userDao.save(newUser);
}
}
My WebSecurityConfig:
#Configuration
#AllArgsConstructor
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {//provides security for endpoints
#Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
#Autowired
private UserDetailsService jwtUserDetailsService;
#Autowired
private JwtRequestFilter jwtRequestFilter;
private final AccountService accountService;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// configure AuthenticationManager so that it knows from where to load
// user for matching credentials
// Use BCryptPasswordEncoder
auth.userDetailsService(jwtUserDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()//So we can send post requests without being rejected(if we using form based indication we want to enable this)
.authorizeRequests()
.antMatchers("/authenticate","/register")
.permitAll()//any request that goes trough that end point we want to allow.
.anyRequest()
.authenticated().and().exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(daoAuthenticationProvider());
}
#Bean
public DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider provider =
new DaoAuthenticationProvider();
provider.setPasswordEncoder(bCryptPasswordEncoder);
provider.setUserDetailsService(accountService);
return provider;
}
}
My RequestFilter:
#Component
public class JwtRequestFilter extends OncePerRequestFilter {
#Autowired
private JwtUserDetailsService jwtUserDetailsService;
#Autowired
private JwtTokenUtil jwtTokenUtil;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String authorizationHeader = request.getHeader("Authorization");
String username = null;
String jwt = null;
if (authorizationHeader != null && authorizationHeader.startsWith("")){
jwt = authorizationHeader.substring(7);
username = jwtTokenUtil.extractUsername(jwt);
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null){
UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);
if (jwtTokenUtil.validateToken(jwt, userDetails)){
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
chain.doFilter(request, response);
}
}
My PasswordEncoder:
#Configuration
public class PasswordEncoder{
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder(){
return new BCryptPasswordEncoder();
}
}
My UserDao
#Entity
#Table(name = "myusers")
public class DAOUser {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column
private String username;
#Column
#JsonIgnore
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
My UserDao Interface
I know the name is confusing, but I just followed the tutorial and he gave it this name, I have not changed it, because I want it to work before I rename files.
#Repository
public interface UserDao extends CrudRepository<DAOUser, Integer> {
DAOUser findByUsername(String username);
}
I think these are all the files you need to help me. if you need more, just ask and I will upload them.
Can anyone help me with this problem?
Thanks!!
if you read the spring documentation on passwords (which you should of done before you ask here)
you will see that the format when storing a password is:
{bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
While when you are storing it you are just storing it and not adding the {bcrypt} prefix to the generated string before you store it.
newUser.setPassword(bcryptEncoder.bCryptPasswordEncoder().encode(user.getPassword()));
So when you are later providing a password, you are getting bad password since spring does not know what encoder to use when decoding the password.

How to implement mutiple http request in spring security

I using spring security and jwt . I want login two way : One way by email, password and one way by phone and passcode. If user login by email when login success spring auto return token to user. It same way when user login by phone. I has write code login by email and it login by success and token return to user but i can't know how to implement by phone and passcode. When i implement by mutiple http login form, spring throw me error look like :
Field authenticationManager in com.baotrung.controller.JwtAuthenticationController required a single bean, but 2 were found:
- emailAuthenticationManager: defined by method 'authenticationManagerBean' in class path resource [com/baotrung/config/MultipleSecurityConfig$ApiConfigurationAdapter.class]
- PhoneAuthenticationManager: defined by method 'authenticationManagerBean' in class path resource [com/baotrung/config/MultipleSecurityConfig$PhoneConfigurationAppter.class]
Action:
Consider marking one of the beans as #Primary, updating the consumer to accept multiple beans, or using #Qualifier to identify the bean that should be consumed
Bellow my code
#Configuration
#EnableWebSecurity
public class MultipleSecurityConfig {
#Bean
public static PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Configuration
#Order(1)
public static class ApiConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
#Autowired
private UserDetailsService jwtUserDetailsService;
#Autowired
private JwtRequestFilter jwtRequestFilter;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// configure AuthenticationManager so that it knows from where to load
// user for matching credentials
// Use BCryptPasswordEncoder
auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
}
#Bean(name = "emailAuthenticationManager")
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
// We don't need CSRF for this example
httpSecurity.csrf().disable()
// dont authenticate this particular request
.authorizeRequests().antMatchers("/authenticate", "/register").permitAll().
// all other requests need to be authenticated
anyRequest().authenticated().and().
// make sure we use stateless session; session won't be used to
// store user's state.
exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// Add a filter to validate the tokens with every request
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
}
#Configuration
#Order(100)
public static class PhoneConfigurationAppter extends WebSecurityConfigurerAdapter {
#Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
#Autowired
private JwtUserPhoneDetailsService jwtUserPhoneDetailsService;
#Autowired
private JwtPhoneRequestFilter jwtPhoneRequestFilter;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// configure AuthenticationManager so that it knows from where to load
// user for matching credentials
// Use BCryptPasswordEncoder
auth.userDetailsService(jwtUserPhoneDetailsService).passwordEncoder(passwordEncoder());
}
#Bean(name = "PhoneAuthenticationManager")
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
// We don't need CSRF for this example
httpSecurity.csrf().disable()
// dont authenticate this particular request
.authorizeRequests().antMatchers("/phone/authentication", "/phone/register").permitAll().
// all other requests need to be authenticated
anyRequest().authenticated().and().
// make sure we use stateless session; session won't be used to
// store user's state.
exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// Add a filter to validate the tokens with every request
httpSecurity.addFilterBefore(jwtPhoneRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
}
}
Entity
User
public class UserDTO {
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
PhoneUser
#Entity
#Table(name = "user_phone")
public class PhoneUser {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column
private String phone;
#Column
#JsonIgnore
private String passcode;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getPasscode() {
return passcode;
}
public void setPasscode(String passcode) {
this.passcode = passcode;
}
}
EmailJwtTokenFilter
#Slf4j
public class JwtAuthenticationFilter extends OncePerRequestFilter {
#Autowired
private JwtTokenProvider tokenProvider;
#Autowired
private CustomUserDetailsService customUserDetailsService;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
try {
String jwt = getJwtFromRequest(request);
if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {
Long userId = tokenProvider.getUserIdFromJWT(jwt);
UserDetails userDetails = customUserDetailsService.loadUserById(userId);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception ex) {
log.error("Could not set user authentication in security context", ex);
}
filterChain.doFilter(request, response);
}
private String getJwtFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7, bearerToken.length());
}
return null;
}
}
PhoneJwtTokenFilter
#Slf4j
public class PhoneJwtAuthenticationFilter extends OncePerRequestFilter {
#Autowired
private JwtTokenProvider tokenProvider;
#Autowired
private CustomUserDetailsService customUserDetailsService;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
try {
String jwt = getJwtFromRequest(request);
if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {
Long userId = tokenProvider.getUserIdFromJWT(jwt);
UserDetails userDetails = customUserDetailsService.loadUserById(userId);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception ex) {
log.error("Could not set user authentication in security context", ex);
}
filterChain.doFilter(request, response);
}
private String getJwtFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7, bearerToken.length());
}
return null;
}
}
AuthenticationController
#RequestMapping(value = "/authenticate", method = RequestMethod.POST)
public ResponseEntity<?> createAuthenticationToken(#RequestBody JwtRequest authenticationRequest) throws Exception {
authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword());
final UserDetails userDetails = userDetailsService
.loadUserByUsername(authenticationRequest.getUsername());
final String token = jwtTokenUtil.generateToken(userDetails);
return ResponseEntity.ok(new JwtResponse(token));
}
#RequestMapping(value = "/register", method = RequestMethod.POST)
public ResponseEntity<?> saveUser(#RequestBody UserDTO user) throws Exception {
return ResponseEntity.ok(userDetailsService.save(user));
}
private void authenticate(String username, String password) throws Exception {
try {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
} catch (DisabledException e) {
throw new Exception("USER_DISABLED", e);
} catch (BadCredentialsException e) {
throw new Exception("INVALID_CREDENTIALS", e);
}
}
I have a question. I need create two class OnePerRequestFilter for email and phone or only one request for both OnePerRequestFilter? . Why spring thow exception authenticationManagerBean ? . And how to impement user login two way : one way in email and one way in phone in same spring security . Thanks you

Spring security & JPA configuration

I have problem with configuration Spring Security and JPA (Spring DATA). I have some application where "/admin" is secured by "ADMIN" role.
When i use inMemoryAuthentication it works as i want:
auth.inMemoryAuthentication().withUser("admin1").password("admin1").roles("ADMIN");
auth.inMemoryAuthentication().withUser("user1").password("user1").roles("USER");
but when I use JPA and UserDetailsService implementation i have 404 Access denied error on the "/admin" page.
SpringSecurity configuration:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetailsService customUserDetailsService;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailsService).passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/**")
.access("hasRole('ADMIN')")
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/j_spring_security_check")
.failureUrl("/login?error")
.usernameParameter("email")
.passwordParameter("password")
.and()
.logout()
.logoutUrl("/j_spring_security_logout")
.logoutSuccessUrl("/login?logout")
.and()
.exceptionHandling()
.accessDeniedPage("/403")
.and()
.csrf();
}
#Bean
public PasswordEncoder passwordEncoder() {
PasswordEncoder encoder = new BCryptPasswordEncoder();
return encoder;
}
}
CustomUserDetailsService.class:
#Service
public class CustomUserDetailsService implements UserDetailsService {
private UserService userService;
#Autowired
public CustomUserDetailsService(UserService userService) {
super();
this.userService = userService;
}
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userService.findUserByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("UserName " + username + " not found");
}
SecurityUser sUser= new SecurityUser(user);
return sUser;
}
}
SecurityUser.class:
public class SecurityUser extends User implements UserDetails {
private static final long serialVersionUID = 1L;
public SecurityUser(User user) {
this.setId(user.getId());
this.setEmail(user.getEmail());
this.setUsername(user.getUsername());
this.setPassword(user.getPassword());
this.setRoles(user.getRoles());
}
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(0);
Set<Role> userRoles = this.getRoles();
if (userRoles != null) {
for (Role role : userRoles) {
SimpleGrantedAuthority authority = new SimpleGrantedAuthority(role.getName());
authorities.add(authority);
}
}
System.out.println("Roles: "+authorities.size());
authorities.stream().forEach(System.out::println);
System.out.println("==========");
return authorities;
}
public String getPassword() {
return super.getPassword();
}
public String getUsername() {
return super.getUsername();
}
public boolean isAccountNonExpired() {
return true;
}
public boolean isAccountNonLocked() {
return true;
}
public boolean isCredentialsNonExpired() {
return true;
}
public boolean isEnabled() {
return true;
}
}
Database:
INSERT INTO `user` (`ID_USER`, `EMAIL`, `PASSWORD`, `USERNAME`) VALUES (1, 'admin#admin.pl', '$2a$10$bRqlytB7SOVw5Y2P8QFjgucN2hjCfdChUw4o.GAAzkaUbQHPklpE2','admin'), (2, 'user#user.pl', '$2a$10$Ydf6v7TX.SjH7FAoDQUQau2yqlLb1.xSsS/IUClUfgizAhqOVEw2C', 'user');
INSERT INTO `role` (`ID_ROLE`, `ROLE_NAME`) VALUES (1, 'ADMIN');
INSERT INTO `user_role` (`ID_USER`, `ID_ROLE`) VALUES (1, 1);
Github:
https://github.com/cinek1992/blog/
Thanks for any help
Your not using the userService in spring security configuration. Add this:
private UserService detailsService;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userService(detailsService).passwordEncoder(passwordEncoder());
}

Authentication with Spring Security + Spring data + MongoDB

I want to use Spring security with MongoDB (using Spring data) and retrieve the users from my own database for spring security. However, I can not do that since my userservice type does not seem to be supported.
This is my UserService class:
public class UserService {
private ApplicationContext applicationContext;
private MongoOperations mongoOperations;
public UserService() {
applicationContext = new AnnotationConfigApplicationContext(MongoConfig.class);
mongoOperations = (MongoOperations) applicationContext.getBean("mongoTemplate");
}
public User find(String username) {
return mongoOperations.findOne(Query.query(Criteria.where("username").is(username)), User.class);
}
}
And my SecurityConfig class:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserService userService;
#Autowired
public void configAuthBuilder(AuthenticationManagerBuilder builder) throws Exception {
builder.userDetailsService(userService); //THIS DOES NOT WORK
builder.inMemoryAuthentication().withUser("username").password("password").roles("USER");
}
}
The line I commented says:
The inferred type UserService is not a valid substitute for the bounded parameter <T extends UserDetailsService>.
How can I fix it so I can retrieve the users from my own database?
Service Layer
You have to create a separate service implementing org.springframework.security.core.userdetails.UserDetailsService and inject it inside the AuthenticationManagerBuilder.
#Component
public class SecUserDetailsService implements UserDetailsService{
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
/*Here add user data layer fetching from the MongoDB.
I have used userRepository*/
User user = userRepository.findByUsername(username);
if(user == null){
throw new UsernameNotFoundException(username);
}else{
UserDetails details = new SecUserDetails(user);
return details;
}
}
}
Model
UserDetails Should be also implemented. This is the POJO which will keep the user authenticated details by the Spring. You may include your Entity data object wrapped inside it, as I have done.
public class SecUserDetails implements UserDetails {
private User user;
public SecUserDetails(User user) {
this.user = user;
}
......
......
......
}
Security Config
Autowire the service that we created before and set it inside the AuthenticationManagerBuilder
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
SecUserDetailsService userDetailsService ;
#Autowired
public void configAuthBuilder(AuthenticationManagerBuilder builder) throws Exception {
builder.userDetailsService(userDetailsService);
}
}
Create your own authentication provider providing a class that extends the UserDetailservice.
Ensure content scanning is enable in your spring context xml file.
<authentication-provider user-service-ref="userModelService">
<password-encoder hash="sha" />
</authentication-provider>
#Service
public class UserModelService implements UserDetailsService
{
#Autowired
private UserModelRepositoryImpl repository;
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
{
UserModel user = repository.findByUsername(username);
if( user == null )
throw new UsernameNotFoundException( "Name not found!" );
List<SimpleGrantedAuthority> authorities = Arrays.asList(new SimpleGrantedAuthority( user.getRole()));
return new User(user.getUsername(), user.getSHA1Password(), authorities );
}
public void saveUserDetails(UserModel userModel)
{
repository.save(userModel);
}
}
This class will enable spring query mongo for the username and password required for authentication. Next create the user model class.
public class UserModel
{
private String id;
#Indexed(unique=true)
private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
Create the user implementation class that extends the DAO.
#Service
public class UserModelService implements UserDetailsService
{
#Autowired
private UserModelRepositoryImpl repository;
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
{
UserModel user = repository.findByUsername(username);
if( user == null )
throw new UsernameNotFoundException( "Oops!" );
List<SimpleGrantedAuthority> authorities = Arrays.asList(new SimpleGrantedAuthority( user.getRole()));
return new User(user.getUsername(), user.getSHA1Password(), authorities );
}
Finally configure mongo and you're done.

Categories

Resources