per-authentication exceptions are not triggers - spring-boot

I have implemented the pre authentication class and working fine, when authentication success, but when custom exception throw error not displays but redirects to the login page. I want to redirect to the error page when custom exception occurred. I used spring security auth2 and spring boot to develop it.
public class CustomPreAuthenticatedAuthenticationProvider extends PreAuthenticatedAuthenticationProvider {
private static final Logger LOG = Logger.getLogger(CustomPreAuthenticatedAuthenticationProvider.class);
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
LOG.info("CustomPreAuthenticatedAuthenticationProvider");
UmUser user = null;
String name = authentication.getName();
// String password = authentication.getCredentials().toString();
// String hashedPwd =null;
user = commonApiCallsService.callUserManagementService(name);
if (null == user) {
throw new CustomException(DataConfig.RES_CODE_INTERNAL_ERROR, name + " User not exist.");
}
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(authentication.getPrincipal(), null, translate(user.getRoles()));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
return authenticationToken;
}
private Collection << ? extends GrantedAuthority > translate(List < Role > roles) {
List < GrantedAuthority > authorities = new ArrayList < > ();
if (null != roles) {
roles.stream().map((role) - > role.getRoleName().toUpperCase()).map((name) - > {
if (!name.startsWith("ROLE_")) {
name = "ROLE_" + name;
}
return name;
}).forEachOrdered((name) - > {
authorities.add(new SimpleGrantedAuthority(name));
});
}
return authorities;
}
}
#Configuration
#Order(1)
#EnableResourceServer
public class ResourceServerInternal extends WebSecurityConfigurerAdapter {
public ResourceServerInternal() {
super();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.antMatcher("/internal/**")
.addFilterAfter(siteminderFilter(), RequestHeaderAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/", "/internal/login*", "/callback/", "/oauth/authorize*", "/exit", "**/logout").permitAll()
.anyRequest().authenticated();
}
#Bean
public FilterRegistrationBean registration(RequestHeaderAuthenticationFilter filter) throws Exception {
FilterRegistrationBean registration = new FilterRegistrationBean(filter);
registration.setEnabled(false);
return registration;
}
#Bean(name = "siteminderFilter")
public RequestHeaderAuthenticationFilter siteminderFilter() throws Exception {
RequestHeaderAuthenticationFilter requestHeaderAuthenticationFilter = new RequestHeaderAuthenticationFilter();
requestHeaderAuthenticationFilter.setPrincipalRequestHeader("SM_USER");
requestHeaderAuthenticationFilter.setAuthenticationManager(authenticationManager());
return requestHeaderAuthenticationFilter;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.parentAuthenticationManager(authenticationManager());
}
#Bean
#Override
protected AuthenticationManager authenticationManager() throws Exception {
final List < AuthenticationProvider > providers = new ArrayList < > (1);
providers.add(preauthAuthProvider());
return new ProviderManager(providers);
}
#Bean(name = "preAuthProvider")
PreAuthenticatedAuthenticationProvider preauthAuthProvider() throws Exception {
CustomPreAuthenticatedAuthenticationProvider provider = new CustomPreAuthenticatedAuthenticationProvider();
provider.setPreAuthenticatedUserDetailsService(userDetailsServiceWrapper());
return provider;
}
#Bean
UserDetailsByNameServiceWrapper < PreAuthenticatedAuthenticationToken > userDetailsServiceWrapper() throws Exception {
UserDetailsByNameServiceWrapper < PreAuthenticatedAuthenticationToken > wrapper = new UserDetailsByNameServiceWrapper < > ();
wrapper.setUserDetailsService(userDetailsService);
return wrapper;
}
#Override
#Order(1)
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/*.css");
web.ignoring().antMatchers("/*.js");
web.ignoring().antMatchers("/css/**");
}
}
public class CustomException extends AuthenticationException {
private Integer errorCode;
private String errorMsg;
public CustomException(Integer errorCode, String errorMsg) {
super(errorMsg);
this.errorCode = errorCode;
this.errorMsg = errorMsg;
}
public Integer getErrorCode() {
return errorCode;
}
public void setErrorCode(Integer errorCode) {
this.errorCode = errorCode;
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
}

I have added `requestHeaderAuthenticationFilter.setContinueFilterChainOnUnsuccessfulAuthentication(false);`
to the siteminderFilter.it works.
#Bean(name = "siteminderFilter")
public RequestHeaderAuthenticationFilter siteminderFilter() throws Exception {
RequestHeaderAuthenticationFilter requestHeaderAuthenticationFilter = new RequestHeaderAuthenticationFilter();
requestHeaderAuthenticationFilter.setPrincipalRequestHeader("user_employee_id");
requestHeaderAuthenticationFilter.setAuthenticationManager(authenticationManager());
requestHeaderAuthenticationFilter.setContinueFilterChainOnUnsuccessfulAuthentication(false);
return requestHeaderAuthenticationFilter;
}

Related

Authentication filter, doesn't work after migration to SecurityFilterChain

I've decided to migrate from extending WebSecurityConfigurerAdapter to SecurityFilterChain, and I've met problems with AuthenticationFilter. (With an old method configuration was working).
Everytime, when I'm trying to login by hitting api with postman (/api/users/login), I'm getting 401 HttpStatus in opposite to my expectations (before migration I was getting jwt tokens.
(Credentials are correct :d)
#Configuration
#EnableWebSecurity
#RequiredArgsConstructor
class SecurityConfiguration {
private final UserDetailsService userDetailsService;
private final SuffixConfiguration suffixConfiguration;
private final AuthorizationService authorizationService;
private final AuthenticationService authenticationService;
private String loginURL = "/api/users/login";
#Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
AuthenticationManager authenticationManager = authenticationManager(http.getSharedObject(AuthenticationConfiguration.class));
AuthenticationFilter authenticationFilter = new AuthenticationFilter(authenticationManager, authenticationService);
authenticationFilter.setFilterProcessesUrl(loginURL);
http.headers().cacheControl();
http.csrf().disable();
http.cors();
http
.authorizeRequests()
.antMatchers(HttpMethod.POST, loginURL).permitAll()
.antMatchers("/api/users/register").permitAll()
.antMatchers("/api/users/refreshToken").permitAll();
http
.addFilter(authenticationFilter)
.addFilterBefore(new AuthorizationFilter(authorizationService), UsernamePasswordAuthenticationFilter.class);
http
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http
.authorizeRequests()
.antMatchers("/api/department/add-moderator")
.hasAnyAuthority("[ROLE_ADMIN]");
return http.build();
}
#Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}
}
Here is the code of AuthorizationService
#Slf4j
#Service
public class AuthorizationServiceImpl implements AuthorizationService {
#Override
public void tryAuthorize(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String token = request.getHeader(AUTHORIZATION).substring(TOKEN_PREFIX.length());
try {
Algorithm algorithm = Algorithm.HMAC256(SECRET.getBytes());
JWTVerifier verifier = JWT.require(algorithm).build();
DecodedJWT decodedJWT = verifier.verify(token);
String username = decodedJWT.getSubject();
String[] roles = decodedJWT.getClaim("roles").asArray(String.class);
Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();
stream(roles).forEach(role -> authorities.add(new SimpleGrantedAuthority(role)));
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, null, authorities);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
filterChain.doFilter(request, response);
} catch (Exception exception) {
log.error("Logging exception: d" + exception.getMessage());
throw exception;
}
}
}
And this is AuthenticationService
#Slf4j
#RequiredArgsConstructor
public class AuthenticationServiceImpl implements AuthenticationService {
private final TokenService tokenService;
private final UserModelMapper userModelMapper;
#Override
public UsernamePasswordAuthenticationToken createUsernameAuthenticationToken(HttpServletRequest request, HttpServletResponse response) {
try {
Map<String, String> requestMap = new ObjectMapper().readValue(request.getInputStream(), Map.class);
String username = requestMap.get("username");
String password = requestMap.get("password");
log.info(username, password);
return new UsernamePasswordAuthenticationToken(username, password);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
#Override
public Map<Object, Object> successfulAuthentication(HttpServletRequest request, HttpServletResponse response, Authentication authResult) {
UserEntity user = (UserEntity) authResult.getPrincipal();
String issuer = request.getRequestURI();
String accessToken = tokenService.generateAccessToken(user, issuer);
String refreshToken = tokenService.generateRefreshToken(user, issuer);
Map<Object, Object> responseBody = new HashMap<>();
responseBody.put("access_token", accessToken);
responseBody.put("refresh_token", refreshToken);
responseBody.put("user", userModelMapper.mapUserEntityToLoginResponseDTO(user));
return responseBody;
}
}
Also as you suggested, code where users are saved
#RequiredArgsConstructor
#Service
#Transactional
class UserManagementServiceImpl implements UserManagementService {
private final UserRepository userRepository;
private final SuffixConfiguration suffixConfiguration;
private final DepartmentFacade departmentFacade;
private final RoleFacade roleFacade;
private final UserFindingService userFindingService;
private final UserModelMapper userModelMapper;
#Override
public UserResponseDTO registerNewUser(RegisterNewUserRequestDTO requestDTO) throws IllegalArgumentException {
checkIfUserWithGivenUsernameAlreadyExists(requestDTO.username());
UserEntity newUserEntity = createEntityToSave(requestDTO);
userRepository.save(newUserEntity);
return userModelMapper.mapUserEntityToUserResponseDTO(newUserEntity);
}
#Override
public void deleteUser(DeleteUserRequestDTO requestDTO) {
UserEntity requestingUser = userFindingService.getUserEntity(requestDTO.username());
List<RoleEntity> allowedRoles = Arrays.asList(roleFacade.findByRoleType(RoleType.ROLE_ADMIN), roleFacade.findByRoleType(RoleType.ROLE_MODERATOR));
if (requestingUser.getRoles().containsAll(allowedRoles)) {
userRepository.deleteByUsername(requestDTO.username());
} else {
throw new UserDoesNotHavePermissionException(requestingUser.getUsername());
}
}
#Override
public LoginResponseDTO login(LoginRequestDTO requestDTO) {
UserEntity userEntity = userFindingService.getUserEntity(requestDTO.username());
isCredentialsCorrect(requestDTO, userEntity);
return userModelMapper.mapUserEntityToLoginResponseDTO(userEntity);
}
private void isCredentialsCorrect(LoginRequestDTO requestDTO, UserEntity userEntity) {
if (!suffixConfiguration.bCryptPasswordEncoder().matches(requestDTO.password(), userEntity.getPassword())) {
throw new BadCredentialsException("Bad credentials");
}
}
private UserEntity createEntityToSave(RegisterNewUserRequestDTO requestDTO) throws IllegalArgumentException {
UserEntity newUserEntity = new UserEntity(requestDTO.username(), encodePassword(requestDTO.password()));
RoleEntity role = roleFacade.createRoleEntity(requestDTO.role());
newUserEntity.getRoles().add(role);
newUserEntity.getDepartmentEntities().add(departmentFacade.getDepartmentEntity(requestDTO.department()));
return newUserEntity;
}
private void checkIfUserWithGivenUsernameAlreadyExists(String username) {
userRepository.findByUsername(username).ifPresent(user -> {
throw new UsernameTakenException(username);
});
}
private String encodePassword(String password) {
if (password != null) {
return suffixConfiguration.bCryptPasswordEncoder().encode(password);
} else {
throw new EmptyPasswordException();
}
}
}
Thanks for help

Spring Boot security sign-in 403 Forbidden error question

I am having some issue on my Spring security sign-in. Signup works fine with no error but only sign-in returns 403 forbidden error.
I tried add http.httpBasic() and it returns 401 error.
I have http.csrf().disable() in the SecurityConfig.java but it still doesn't work even it's permitAll() condition. I am stuck in this problem for days :/ I tried every single solution that I googled but nothing worked.
Here is SecurityConfig.java
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors();//cross-origin-resource-sharing
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.authorizeRequests()
.antMatchers("/api/authentication/**").permitAll()//login and register pre-path
.anyRequest().permitAll();
http.addFilterBefore(jwtAuthorizationFilter(), UsernamePasswordAuthenticationFilter.class);
}
#Bean
public JwtAuthorizationFilter jwtAuthorizationFilter()
{
return new JwtAuthorizationFilter();
}
#Override
#Bean(BeanIds.AUTHENTICATION_MANAGER)
public AuthenticationManager authenticationManagerBean() throws Exception
{
return super.authenticationManagerBean();
}
#Bean
public PasswordEncoder passwordEncoder()
{
return new BCryptPasswordEncoder();
}
#Bean
public WebMvcConfigurer corsConfigurer()
{
return new WebMvcConfigurer()
{
#Override
public void addCorsMappings(CorsRegistry registry)
{
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("*");
}
};
}
I think from this jwtAutheorizationFiler.java cause the issue if the Security config is fine:
public class JwtAuthorizationFilter extends OncePerRequestFilter
{
#Autowired
private JwtProvider jwtProvider;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException
{
Authentication authentication = jwtProvider.getAuthentication(request);
if (authentication != null && jwtProvider.isTokenValid(request))
{
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
}
}
SecurityUtils.java
public class SecurityUtils
{
public static final String ROLE_PREFIX = "ROLE_";
public static final String AUTH_HEADER = "authorization";
public static final String AUTH_TOKEN_HEADER = "Bearer";
public static final String AUTH_TOKEN_PREFIX = AUTH_TOKEN_HEADER + " ";
public static SimpleGrantedAuthority convertToAuthority(String role)
{
String formattedRole = role.startsWith(ROLE_PREFIX) ? role : ROLE_PREFIX + role;
return new SimpleGrantedAuthority(formattedRole);
}
public static String extractAuthTokenFromRequest(HttpServletRequest request)
{
String bearerToken = request.getHeader(AUTH_HEADER);
if(StringUtils.hasLength(bearerToken) && bearerToken.startsWith(AUTH_TOKEN_PREFIX))
{
return bearerToken.substring(7);
}
return null;
}
}
CustomUserDetailService.java :
#Service
public class CustomUserDetailsService implements UserDetailsService
{
private LoginService loginService;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
{
Login login = loginService.findByUsername(username)
.orElseThrow(()-> new UsernameNotFoundException("User not found with username: "+ username));
Set<GrantedAuthority> authorities = Set.of(SecurityUtils.convertToAuthority(login.getRole().name()));
return UserPrincipal.builder()
.login(login)
.id(login.getId())
.username(login.getUsername())
.password(login.getPassword())
.authorities(authorities)
.build();
}
}
AuthenticationController.java
#Autowired
private AuthenticationService authenticationService;
#Autowired
private LoginService loginService;
#Autowired
private JwtRefreshTokenService jwtRefreshTokenService;
#PostMapping("sign-up")//api/authentication/sign-up
public ResponseEntity<?> signUp(#RequestBody Login login)
{
if(loginService.findByUsername(login.getUsername()).isPresent())
{
return new ResponseEntity<>(HttpStatus.CONFLICT);
}
return new ResponseEntity<>(loginService.saveLogin(login), HttpStatus.CREATED);
}
#PostMapping("sign-in")//api/authentication/sign-in
public ResponseEntity<?> signIn(#RequestBody Login login)
{
return new ResponseEntity<>(authenticationService.signInAndReturnJWT(login), HttpStatus.OK);
}
AuthenticationServiceImple.java
#Service
public class AuthenticationServiceImpl implements AuthenticationService
{
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private JwtProvider jwtProvider;
#Autowired
private JwtRefreshTokenService jwtRefreshTokenService;
#Override
public Login signInAndReturnJWT(Login signInRequest)
{
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(signInRequest.getUsername(), signInRequest.getPassword())
);
UserPrincipal userPrincipal = (UserPrincipal) authentication.getPrincipal();
String jwt = jwtProvider.generateToken(userPrincipal);
Login signInUser = userPrincipal.getLogin();
signInUser.setAccessToken(jwt);
signInUser.setRefreshToken(jwtRefreshTokenService.createRefreshToken(signInUser.getId()).getTokenId());
return signInUser;
}
}

Get Forbidden error in spring security role based authentication and authorization

I am trying to learn Spring security where I am facing a problem with Role based security.
There are 2 tables User and Role having One to Many realtions.
When I enter username and password in spring default form, I successfully fetch correct Users deatails through loadUserByUserName() method. But on screen I get a message like
This application has no explicit mapping for /error, so you are seeing
this as a fallback.
There was an unexpected error (type=Forbidden, status=403). Forbidden
Only #GetMapping("/user") method works properly.
Here is the controller part
#RestController
#RequestMapping("/admin")
public class AdminController {
#Autowired
UserRepository userRepo;
#Autowired
RoleRepository roleRepo;
#PreAuthorize("hasAnyRole('ADMIN')")
#PostMapping("/add")
public String addUserByAdmin(#RequestBody User user)
{
user.getRoles().forEach(role -> role.setUser(user));
userRepo.save(user);
return "User added Successfully";
}
#PreAuthorize("hasAnyRole('ADMIN')")
#GetMapping("/process")
public String process()
{
return "Processing....";
}
#GetMapping("/user")
public String users() // This code is working properly
{
System.out.println("U r in user area's");
return "User's space";
}
}
Configuration part
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableJpaRepositories(basePackageClasses = UserRepository.class)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
protected void configure(HttpSecurity http)throws Exception
{
http.csrf().disable();
http.authorizeRequests()
.antMatchers("/admin/**").authenticated()
.anyRequest().permitAll()
.and()
.formLogin().permitAll();
}
}
Service part
#Service
public class CustomeUserDetailsService implements UserDetailsService
{
#Autowired
UserRepository userRepo;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User u = userRepo.findByName(username);
CustomeUserDetails cUserDetails = new CustomeUserDetails();
if(u == null)
{
throw new UsernameNotFoundException("User "+username +"not found");
}
else
{
cUserDetails.setUser(u);
}
return cUserDetails;
}
}
Where am I wrong ?
What if I want to add one more URL which does not require any authnetication and authorization , how to add it ?
I think you should use "ROLE_ADMIN" instead of "ADMIN". The "ROLE_ADMIN" key can be saved in the database and turned into Collection. Below how I did.
CLASS THAT IMPLEMENTS USERDETAILS
public class UsuarioSS implements LdapUserDetails {
private static final long serialVersionUID = 1164806375870272028L;
private String cdusuariorede;
private Collection<GrantedAuthority> authorities;
public UsuarioSS() {
}
public UsuarioSS(String cdusuariorede,List<Perfil> perfis) {
super();
this.cdusuariorede = cdusuariorede;
this.authorities = new ArrayList<GrantedAuthority>();
for (Perfil perfil : perfis) {
this.authorities.add(new SimpleGrantedAuthority(perfil.toString()));
}
}
public String getId() {
return cdusuariorede;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
#Override
public String getPassword() {
return null;
}
#Override
public String getUsername() {
return cdusuariorede;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
public boolean hasRole(String springSecurityKey) {
return getAuthorities().contains(new SimpleGrantedAuthority(springSecurityKey));
}
#Override
public void eraseCredentials() {
// TODO Auto-generated method stub
}
#Override
public String getDn() {
// TODO Auto-generated method stub
return null;
}
}
ENUM THAT REPRESENTS MY USER PROFILES (AUTHORITHYES)
public enum Perfil {
ROLE_ADMIN,ROLE_DEFAULT
}
A AUTHORIZATION FILTER
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
private JWTUtil jwtUtil;
private UserDetailsService userDetailsService;
public JWTAuthorizationFilter(AuthenticationManager authenticationManager, JWTUtil jwtUtil,
UserDetailsService userDetailsService) {
super(authenticationManager);
this.jwtUtil = jwtUtil;
this.userDetailsService = userDetailsService;
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
String header = request.getHeader("Authorization");
if (header != null && header.startsWith("Bearer ")) {
UsernamePasswordAuthenticationToken auth = getAuthentication(header.substring(7));
if (auth != null) {
SecurityContextHolder.getContext().setAuthentication(auth);
}
}
chain.doFilter(request, response);
}
private UsernamePasswordAuthenticationToken getAuthentication(String token) {
if (jwtUtil.tokenValido(token)) {
String login = jwtUtil.getLogin(token);
UserDetails usuario = userDetailsService.loadUserByUsername(login);
return new UsernamePasswordAuthenticationToken(usuario, null, usuario.getAuthorities());
}
return null;
}
}
MY CONTROLLER
#RestController
#RequestMapping(value = "/parte")
public class ParteController {
#Autowired
private ParteService service;
#PreAuthorize("hasAnyRole('ROLE_ADMIN')")
#GetMapping("/partes_demandadas_por_processo")
public ResponseEntity<List<TpPartesProcessoDto>> getPartesPorNuprocesso(
#RequestParam(name = "processo",required = true)
#Length(max = 15,min = 15,message = "O campo processo deve possuir 15 caracteres.")
String processo
) throws SQLException{
List<TpPartesProcessoDto> partes = service.getPartesdoProcessoPorNuprocesso(processo);
return ResponseEntity.ok().body(partes);
}
}

NullPointerException for checking token in Spring Security

I'm using jwt token for authorization and when I send a request with the token I get null pointer exception and 500 error. How to fix this? I can get token for authorization, but can't use its.
Error:
java.lang.NullPointerException: null
at com.ilya.testapp.security.JwtTokenProvider.getAuthentication(JwtTokenProvider.java:64) ~[classes/:na]
at com.ilya.testapp.security.JwtTokenFilter.doFilter(JwtTokenFilter.java:29) ~[classes/:na]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) ~[spring-security-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
...
Controller for login:
#RestController
#RequestMapping(value = "/api/v1/auth/")
public class AuthenticationRestControllerV1 {
private final AuthenticationManager authenticationManager;
private final JwtTokenProvider jwtTokenProvider;
private final UserRepository userRepository;
public AuthenticationRestControllerV1(AuthenticationManager authenticationManager, JwtTokenProvider jwtTokenProvider, UserRepository userRepository) {
this.authenticationManager = authenticationManager;
this.jwtTokenProvider = jwtTokenProvider;
this.userRepository = userRepository;
}
#PostMapping("login")
public ResponseEntity login(#RequestBody AuthenticationRequestDto requestDto) {
try {
String username = requestDto.getUsername();
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, requestDto.getPassword()));
User user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("User with username: " + username + " not found");
}
String token = jwtTokenProvider.createToken(username, user.getRoleList());
Map<Object, Object> response = new HashMap<>();
response.put("username", username);
response.put("token", token);
return ResponseEntity.ok(response);
} catch (AuthenticationException e) {
throw new BadCredentialsException("Invalid username or password");
}
}
}
SecurityConfig:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final JwtTokenProvider jwtTokenProvider;
public SecurityConfig(JwtTokenProvider jwtTokenProvider) {
this.jwtTokenProvider = jwtTokenProvider;
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/api/v1/auth/login").permitAll()
.antMatchers("/api/v1/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.apply(new JwtConfigurer(jwtTokenProvider));
}
#Bean
public CorsConfigurationSource corsConfigurationSource() {
final CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(List.of("*"));
configuration.setAllowedMethods(List.of("HEAD",
"GET", "POST", "PUT", "DELETE", "PATCH"));
configuration.setAllowCredentials(true);
configuration.setAllowedHeaders(List.of("Authorization", "Cache-Control", "Content-Type"));
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
JwtTokenFilter:
public class JwtTokenFilter extends GenericFilterBean {
private JwtTokenProvider jwtTokenProvider;
public JwtTokenFilter(JwtTokenProvider jwtTokenProvider) {
this.jwtTokenProvider = jwtTokenProvider;
}
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain)
throws IOException, ServletException {
String token = jwtTokenProvider.resolveToken((HttpServletRequest) req);
System.out.println("req = "+req+" token1 = "+token);
if (token != null && jwtTokenProvider.validateToken(token)) {
Authentication auth = jwtTokenProvider.getAuthentication(token);
if (auth != null) {
SecurityContextHolder.getContext().setAuthentication(auth);
}
}
filterChain.doFilter(req, res);
}
}
JwtTokenProvider:
#Component
public class JwtTokenProvider {
#Value("${jwt.token.secret}")
private String secret;
#Value("${jwt.token.expired}")
private long validityInMilliseconds;
private UserPrincipalDetailsService userPrincipalDetailsService;
public JwtTokenProvider() {
}
public JwtTokenProvider(UserPrincipalDetailsService userPrincipalDetailsService) {
this.userPrincipalDetailsService = userPrincipalDetailsService;
}
public JwtTokenProvider(String secret, long validityInMilliseconds, UserPrincipalDetailsService userPrincipalDetailsService) {
this.secret = secret;
this.validityInMilliseconds = validityInMilliseconds;
this.userPrincipalDetailsService = userPrincipalDetailsService;
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
return bCryptPasswordEncoder;
}
public String createToken(String username, List<String> roles){
Claims claims = Jwts.claims().setSubject(username);
claims.put("roles",roles);
Date now = new Date();
Date validity = new Date(now.getTime() + validityInMilliseconds);
return Jwts.builder()//
.setClaims(claims)//
.setIssuedAt(now)//
.setExpiration(validity)//
.signWith(SignatureAlgorithm.HS256, secret)//
.compact();
}
public Authentication getAuthentication(String token) {
UserDetails userDetails = this.userPrincipalDetailsService.loadUserByUsername(getUsername(token));
return new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
}
public String getUsername(String token){
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody().getSubject();
}
public boolean validateToken(String token) {
try {
Jws<Claims> claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token);
if (claims.getBody().getExpiration().before(new Date())) {
return false;
}
return true;
} catch (JwtException | IllegalArgumentException e) {
throw new JwtAuthenticationException("JWT token is expired or invalid");
}
}
public String resolveToken(HttpServletRequest req) {
String bearerToken = req.getHeader("Authorization");
if (bearerToken != null && bearerToken.startsWith("Bearer_")) {
return bearerToken.substring(7, bearerToken.length());
}
return null;
}
}
JwtConfigurer:
public class JwtConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
private JwtTokenProvider jwtTokenProvider;
public JwtConfigurer(JwtTokenProvider jwtTokenProvider) {
this.jwtTokenProvider = jwtTokenProvider;
}
#Override
public void configure(HttpSecurity httpSecurity) throws Exception {
JwtTokenFilter jwtTokenFilter = new JwtTokenFilter(jwtTokenProvider);
httpSecurity.addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class);
}
}
UserPrincipalDetailService:
#Service
public class UserPrincipalDetailsService implements UserDetailsService {
private final UserRepository userRepository;
public UserPrincipalDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = this.userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("User with username: " + username + " not found");
}
UserPrincipal userPrincipal = new UserPrincipal(user);
return userPrincipal;
}
}
UserPrincipal:
public class UserPrincipal implements UserDetails {
private User user;
public UserPrincipal(User user){
this.user = user;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> authorities = new ArrayList<>();
// Extract list of permissions (name)
this.user.getPermissionList().forEach(p -> {
GrantedAuthority authority = new SimpleGrantedAuthority(p);
authorities.add(authority);
});
// Extract list of roles (ROLE_name)
this.user.getRoleList().forEach(r -> {
GrantedAuthority authority = new SimpleGrantedAuthority("ROLE_" + r);
authorities.add(authority);
});
return authorities;
}
#Override
public String getPassword() {
return this.user.getPassword();
}
#Override
public String getUsername() {
return this.user.getUsername();
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return this.user.getActive() == 1;
}
}
Hard to tell, because you deleted all line-numbers. But in the end you have a NullPointerException in your getAuthentication method in JWTTokenprovider. Is your getUsername(token) method broken?

Custom Spring Security OAuth2 with Spring Social integration

Custom Spring security OAuth2 is working fine and now would like to add Spring Social integration(facebook login, google login etc), When the user clicks on Facebook login(user would not provide any username/password), Facebook will return an access_token, but this access_token we can not use to query my application web services, to get my application access_token we need to pass username and password with grant_type as password. Below are my configuration files
AuthorizationServerConfiguration.java
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
#Autowired
DataSource dataSource;
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Override
public void configure(
AuthorizationServerSecurityConfigurer oauthServer)
throws Exception {
oauthServer.allowFormAuthenticationForClients();
}
#Override
public void configure(ClientDetailsServiceConfigurer clients)
throws Exception {
clients.jdbc(dataSource);
}
#Bean
#Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setSupportRefreshToken(true);
tokenServices.setTokenStore(tokenStore());
tokenServices.setAccessTokenValiditySeconds(86400000);
tokenServices.setRefreshTokenValiditySeconds(86400000);
return tokenServices;
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints
.tokenServices(tokenServices())
.authenticationManager(authenticationManager);
}
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
}
ResourceServerConfiguration.java
#Configuration
#EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
private String resourceId = "rest_api";
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
// #formatter:off
resources.resourceId(resourceId);
// #formatter:on
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/oauth/token").permitAll()
.antMatchers(HttpMethod.GET, "/**/login").permitAll()
.antMatchers(HttpMethod.GET, "/**/callback").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().permitAll();
}
}
and finally WebSecurityConfigurerAdapter.java
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.userDetailsService(userDetailsService);
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean()
throws Exception {
return super.authenticationManagerBean();
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/oauth/token").permitAll()
.antMatchers(HttpMethod.GET, "/**/login").permitAll()
.antMatchers(HttpMethod.GET, "/**/callback").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().permitAll();
}
}
Have read different posts in SO, but couldn't get any working example, please guide me on this. Thanks in advance.!
String redirectURL = messages.getProperty(Constant.REDIRECT_URI.getValue());
String clientSecret = messages.getProperty(Constant.CLIENT_SECRET.getValue());
HttpHeaders header = new HttpHeaders();
header.setContentType(org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED);
String req = "client_id=myas&" + "client_secret=" + clientSecret + "&grant_type=authorization_code&"
+ "scope=user_profile&" + "code=" + loginReqeust.getCode() + "&redirect_uri="
+ loginReqeust.getRedirectURL();
HttpEntity<String> body = new HttpEntity<String>(req, header);
Map<Object, Object> mapRes = new LinkedHashMap<Object, Object>();
// call to get access token
mapRes = getEndpoint("https://auth.mygov.in/oauth2/token", null, body, null);
String accessToken = mapRes.get("access_token").toString();
// Call for getting User Profile
String userUrl = "https://auth.mygov.in/myasoauth2/user/profile";
HttpHeaders head = new HttpHeaders();
head.add("Authorization", "Bearer " + accessToken);
HttpEntity<String> ent = new HttpEntity<String>(head);
Map<Object, Object> mapResponse = new LinkedHashMap<Object, Object>();
mapResponse.put("userProfile", getEndpoint(userUrl, null, ent, null));
//In my case userKey represents the username basically the email of the user using which he/she logged into facebook/google
String userKey = (String) ((LinkedHashMap<Object, Object>) mapResponse.get("userProfile")).get("mail");
// Store the user profile in your database with basic info like username & an autogenerated password for the time being and other basic fields.
userService.save(userprofileInfo);
mapResponse.put("username", "retrieved from facebook/google user's profile");
mapResponse.put("password", "autogenerated by your application");
//send back this response (mapResponse) to your UI and then from there make a call by passing this username and pwd to retrieve the access_token from your own applicatioon.
I had a similar requirement to get an access token from facebook and generate own JWT token by validating the facebook token on the server side.
I modified the project mentioned here:
https://github.com/svlada/springboot-security-jwt
My customizations are as follows(I am assuming you already have a facebook access token):
LoginRequest.java
public class LoginRequest {
private String token;
#JsonCreator
public LoginRequest(#JsonProperty("token") String token) {
this.token = token;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
}
AjaxLoginProcessingFilter.java
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException, ServletException {
if (!HttpMethod.POST.name().equals(request.getMethod()) || !WebUtil.isAjax(request)) {
if(logger.isDebugEnabled()) {
logger.debug("Authentication method not supported. Request method: " + request.getMethod());
}
throw new AuthMethodNotSupportedException("Authentication method not supported");
}
LoginRequest loginRequest = objectMapper.readValue(request.getReader(), LoginRequest.class);
if (StringUtils.isBlank(loginRequest.getToken())) {
throw new AuthenticationServiceException("token not provided");
}
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(loginRequest.getToken(), null);
return this.getAuthenticationManager().authenticate(token);
}
AjaxAuthenticationProvider.java
#Component
public class AjaxAuthenticationProvider implements AuthenticationProvider {
#Autowired private BCryptPasswordEncoder encoder;
#Autowired private DatabaseUserService userService;
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
Assert.notNull(authentication, "No authentication data provided");
String username = null;
try {
username = getUsername(authentication.getPrincipal());
} catch (UnsupportedOperationException e) {
} catch (IOException e) {
}
//You can either register this user by fetching additional data from facebook or reject it.
User user = userService.getByUsername(username).orElseThrow(() -> new UsernameNotFoundException("User not found"));
if (user.getRoles() == null) throw new InsufficientAuthenticationException("User has no roles assigned");
List<GrantedAuthority> authorities = user.getRoles().stream()
.map(authority -> new SimpleGrantedAuthority(authority.getRole().authority()))
.collect(Collectors.toList());
UserContext userContext = UserContext.create(user.getUsername(), authorities);
return new UsernamePasswordAuthenticationToken(userContext, null, userContext.getAuthorities());
}
private String getUsername(Object principal) throws UnsupportedOperationException, IOException {
HttpClient client = new DefaultHttpClient();
//I am just accessing the details. You can debug whether this token was granted against your app.
HttpGet get = new HttpGet("https://graph.facebook.com/me?access_token=" + principal.toString());
HttpResponse response = client.execute(get);
BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
StringBuffer result = new StringBuffer();
String line = "";
while ((line = rd.readLine()) != null) {
result.append(line);
}
JSONObject o = new JSONObject(result.toString());
//This is just for demo. You should use id or some other unique field.
String username = o.getString("first_name");
return username;
}
#Override
public boolean supports(Class<?> authentication) {
return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}
}
Apart from this, I also had to add custom BeanPostProcessor to override the default behavior of UsernamePasswordAuthenticationFilter to accept only token as a field instead of a username and a password.
UserPassAuthFilterBeanPostProcessor.java
public class UserPassAuthFilterBeanPostProcessor implements BeanPostProcessor {
private String usernameParameter;
private String passwordParameter;
#Override
public final Object postProcessAfterInitialization(final Object bean,
final String beanName) {
return bean;
}
#Override
public final Object postProcessBeforeInitialization(final Object bean,
final String beanName) {
if (bean instanceof UsernamePasswordAuthenticationFilter) {
final UsernamePasswordAuthenticationFilter filter =
(UsernamePasswordAuthenticationFilter) bean;
filter.setUsernameParameter(getUsernameParameter());
filter.setPasswordParameter(getPasswordParameter());
}
return bean;
}
public final void setUsernameParameter(final String usernameParameter) {
this.usernameParameter = usernameParameter;
}
public final String getUsernameParameter() {
return usernameParameter;
}
public final void setPasswordParameter(final String passwordParameter) {
this.passwordParameter = passwordParameter;
}
public final String getPasswordParameter() {
return passwordParameter;
}
Configuration:
#Bean
public UserPassAuthFilterBeanPostProcessor userPassAuthFilterBeanPostProcessor(){
UserPassAuthFilterBeanPostProcessor bean = new UserPassAuthFilterBeanPostProcessor();
bean.setUsernameParameter("token");
bean.setPasswordParameter(null);
return bean;
}

Resources