I am getting error in configure method of SecurityConfig class whose code is here:
package com.sumit.Security;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import com.sumit.Repo.UserRepository;
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsService userDetailsService;
#Autowired
private UserRepository userRepository;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
// remove csrf and state in session because in jwt we do not need them
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilter(new JwtAuthenticationFilter(authenticationManager()))
.addFilter(new JwtAuthorizationFilter(authenticationManager(),userRepository))
.authorizeRequests()
// configure access rules
.antMatchers("/register").permitAll()
.antMatchers("/admin").hasRole("ADMIN")
.antMatchers("/user").hasAnyRole("ADMIN","USER")
.antMatchers("/management").hasAnyRole("MANAGER","ADMIN")
.antMatchers("/page1").hasAnyAuthority("ROLE_ADMIN","ACCESS_PAGE1")
.antMatchers("/page2").hasAnyAuthority("ROLE_ADMIN","ACCESS_PAGE2")
.anyRequest().authenticated();
// .and()
// .formLogin()
// .loginPage("/login")
// .usernameParameter("username")
// .passwordParameter("password").successForwardUrl("/welcome")
// .permitAll();
}
#Bean
DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
daoAuthenticationProvider.setUserDetailsService(this.userDetailsService);
return daoAuthenticationProvider;
}
#Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
in above code, error lies here auth.authenticationProvider(authenticationProvider());
code is getting compiled fine but due to this i am getting 403 forbidden. Flow is getting broken here
JwtAutheticationFilter class is here
package com.sumit.Security;
import static com.auth0.jwt.algorithms.Algorithm.HMAC512;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import com.auth0.jwt.JWT;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sumit.Repo.UserRepository;
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private AuthenticationManager authenticationManager;
public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
LoginModel credentials = null;
try {
credentials = new ObjectMapper().readValue(request.getInputStream(), LoginModel.class);
} catch (Exception e) {
e.printStackTrace();
}
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
credentials.getUsername(), credentials.getPassword(), new ArrayList<>());
Authentication auth = authenticationManager.authenticate(usernamePasswordAuthenticationToken);
return auth;
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
Authentication authResult) throws IOException, ServletException {
UserPrincipal principal = (UserPrincipal) authResult.getPrincipal();
String token = JWT.create()
.withSubject(principal.getUsername())
.withExpiresAt(new Date(System.currentTimeMillis() + JWTProperties.EXPIRATION_TIME))
.sign(HMAC512(JWTProperties.SECRET.getBytes()));
response.addHeader(JWTProperties.HEADER_STRING, JWTProperties.TOKEN_PREFIX + token);
}
}
JwtAuthorizationFilter class is here
package com.sumit.Security;
import static com.auth0.jwt.algorithms.Algorithm.HMAC512;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import com.auth0.jwt.JWT;
import com.sumit.Repo.UserRepository;
public class JwtAuthorizationFilter extends BasicAuthenticationFilter {
#Autowired
private UserRepository userRepository;
public JwtAuthorizationFilter(AuthenticationManager authenticationManager,UserRepository userRepository
) {
super(authenticationManager);
}
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
// Read the Authorization header, where the JWT token should be
String header = request.getHeader(JWTProperties.HEADER_STRING);
// If header does not contain BEARER or is null delegate to Spring impl and exit
if (header == null || !header.startsWith(JWTProperties.TOKEN_PREFIX)) {
chain.doFilter(request, response);
return;
}
// If header is present, try grab user principal from database and perform authorization
Authentication authentication = getUsernamePasswordAuthentication(request);
SecurityContextHolder.getContext().setAuthentication(authentication);
// Continue filter execution
chain.doFilter(request, response);
}
private Authentication getUsernamePasswordAuthentication(HttpServletRequest request) {
String token = request.getHeader(JWTProperties.HEADER_STRING)
.replace(JWTProperties.TOKEN_PREFIX,"");
if (token != null) {
// parse the token
String userName = JWT.require(HMAC512(JWTProperties.SECRET.getBytes()))
.build()
.verify(token)
.getSubject();
if (userName != null) {
User user = userRepository.findByUsername(userName);
UserPrincipal principal = new UserPrincipal(user);
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(userName, null, principal.getAuthorities());
return auth;
}
return null;
}
return null;
}
}
User registration process is working fine but authentication is not working because of above mentioned code.
I am using postman to verify it.
Don't know what trouble i am facing and totally frustrated.
Related
I am creating backend app and testing as well. my register url is resposing 403 forbidden but csrf is disabled. I can't fix this problem. Please help me P
Here is my configuration
package uz.renessans.configuration;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import uz.renessans.entity.user.UserRepository;
import uz.renessans.security.JwtFilter;
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
#RequiredArgsConstructor
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private final JwtFilter jwtFilter;
private final UserRepository userRepository;
#Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
UserDetailsService userDetailsService = username ->
userRepository.findByUsername(username).orElseThrow(() -> new UsernameNotFoundException(username));
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
String[] OPEN_URLS = {
"/api/v1/register/**",
"/swagger-ui/**",
"/api/v1/login"
};
http
.csrf()
.disable()
.httpBasic()
.disable()
.authorizeRequests()
.antMatchers(
"/api/v1/register/**",
"/swagger-ui/**",
"/api/v1/login"
)
.permitAll()
.anyRequest()
.authenticated();
// .permitAll();
http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
}
}
Here is my jwt filter class:
package uz.renessans.security;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import uz.renessans.entity.user.User;
import uz.renessans.entity.user.UserRepository;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Optional;
#Component
#RequiredArgsConstructor
public class JwtFilter extends OncePerRequestFilter {
private final JwtProvider jwtProvider;
private final UserRepository userRepository;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String authorization = request.getHeader("Authorization");
if (authorization != null && authorization.startsWith("Bearer") && authorization.length() > 7) {
authorization = authorization.substring(7);
String username = jwtProvider.getPhoneNumber(authorization);
if (username != null) {
Optional<User> optionalUser = userRepository.findByUsername(username);
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = null;
if (optionalUser.isPresent()) {
usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(optionalUser.get(), null, optionalUser.get().getAuthorities());
}
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
filterChain.doFilter(request, response);
}
}
Here is my register api
public HttpEntity<?> register(RegisterDto dto, Role role) {
if (userRepository.existsByUsername(dto.getUsername()))
return new ResponseEntity<>(new ApiResponse("Username already exists", false), HttpStatus.CONFLICT);
if (!dto.getPassword().equals(dto.getRePassword()))
return new ResponseEntity<>(new ApiResponse("Password must match", false), HttpStatus.CONFLICT);
User user = new User();
user.setRole(role);
user.setUsername(dto.getUsername());
user.setPassword(passwordEncoder.encode(dto.getPassword()));
user.setFullName(dto.getFullName());
User savedUser = userRepository.save(user);
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
savedUser.getUsername(), savedUser.getPassword());
authenticationManager.authenticate(authenticationToken);
String token = jwtProvider.generateToken(savedUser.getUsername(), savedUser.getRole());
Response response = Response.builder().token(token).role(role.toString()).build();
return new ResponseEntity<>(new ApiResponse("Registered Successfully", true, response), HttpStatus.OK);
}
I have been working on Spring Security
I use a role based authentication. If the user is admin he will be directed to admindashboard and user will be redirected to his respective dashboard where they share a common login portal.
If a person enters a url of the admin/user dashboard before login it asks the person to login but doesn't redirect to entered url. Instead throws an error "cannot call sendRedirect".
My code for security configuration file
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.web.util.UrlPathHelper;
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Autowired private LoginSuccessHandler loginSuccessHandler;
#Bean
AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider provider=new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
provider.setPasswordEncoder(new BCryptPasswordEncoder());
return provider;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/").permitAll()
.antMatchers("/userhome").hasAuthority("USER").
antMatchers("/adminhome").hasAuthority("ADMIN")
.antMatchers("/register").permitAll()
.and().formLogin().loginPage("/login")
.successHandler(loginSuccessHandler)
.permitAll().and().logout().logoutSuccessHandler(new LogoutSuccessHandler() {
#Override
public void onLogoutSuccess(HttpServletRequest request,HttpServletResponse response,Authentication authentication) throws IOException,ServletException{
System.out.println("The User "+authentication.getName() + " has logged out");
UrlPathHelper helper=new UrlPathHelper();
String context=helper.getContextPath(request);
response.sendRedirect(context+"/home");
}
}).permitAll();
http.csrf().disable();
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
My Login Successful handler
package strictly.cinema.config;
import java.io.IOException;
import java.util.Collection;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import strictly.cinema.service.CustomUserDetails;
#Component
public class LoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler{
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws ServletException, IOException {
CustomUserDetails userDetails = (CustomUserDetails) authentication.getPrincipal();
Collection<? extends GrantedAuthority> authorities =userDetails.getAuthorities();
authorities.forEach(auth->System.out.println(auth.getAuthority()));
String redirectURL=request.getContextPath();
if(userDetails.hasRole("USER"))
redirectURL+="/userhome";
else if(userDetails.hasRole("ADMIN"))
redirectURL+="/adminhome";
response.sendRedirect(redirectURL);
super.onAuthenticationSuccess(request, response, authentication);
}
}
Overriden the logout handler in the security handler itself.
Need help to store the entered URL once the user is authenticated and eligible to access the URL he should be redirected to the URL after the login
Instead of using successHandler, you could redirect him to a success endpoint as follow :
.defaultSuccessUrl("/loginSuccess")
And the new endpoint will redirect him to "/userhome" or "/adminhome" based on his role.
(Below was not tested)
Success endpoint :
#GetMapping("/loginSuccess")
public void getLoginInfo(
#AuthenticationPrincipal UserDetails authentication,
HttpServletResponse response) throws IOException {
if (authentication.getAuthorities()
.contains(new SimpleGrantedAuthority("ADMIN"))) {
response.sendRedirect("/adminhome");
} else {
response.sendRedirect("/userhome");
}
}
I am trying to validate a captcha image before validating the user.
In order to validate the captcha I have created the following class that will process POST requests from /mvc/login
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
public class CaptchaAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private final Logger logger = LoggerFactory.getLogger(getClass());
private String processUrlString;
protected CaptchaAuthenticationFilter(String defaultFilterProcessesUrl) {
super(defaultFilterProcessesUrl);
this.processUrlString=defaultFilterProcessesUrl;
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req =(HttpServletRequest) request;
HttpServletResponse res =(HttpServletResponse) response;
logger.info("doFilter.method " + req.getMethod() + " .doFilter.getRequestURI " + req.getRequestURI());
if(processUrlString.equals(req.getRequestURI()) && "POST".equalsIgnoreCase(req.getMethod())) {
String expected = req.getSession().getAttribute("captcha").toString();
req.getSession().removeAttribute("captcha");
if (expected!=null && !expected.equals(req.getParameter("answer"))) {
unsuccessfulAuthentication(req, res, new InsufficientAuthenticationException("Wrong code"));
return;
}
}
chain.doFilter(request, response);
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException, ServletException {
return null;
}
}
In order to associate the filter to the requests I have created the following configuration class, where I specified the filter to use with: addFilterBefore
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.User.UserBuilder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(new CaptchaAuthenticationFilter("/mvc/login"), UsernamePasswordAuthenticationFilter.class);
http.authorizeRequests()
.antMatchers("/assets/**").permitAll()
.antMatchers(HttpMethod.POST, "/mvc/login").permitAll() // Is this necessary?
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/mvc/index")
.usernameParameter("j_user")
.passwordParameter("j_password")
.permitAll()
.and()
.logout()
.permitAll();
}
#Bean
public UserDetailsService users() {
UserBuilder users = User.withDefaultPasswordEncoder();
UserDetails user = users
.username("user")
.password("password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
}
Am I following the right path in order to validate the request? Or should I follow a different strategy?
One issue that I have detected is that with every logging attempt it is displaying the following request doFilter.method POST .doFilter.getRequestURI /mvc/error instead of doFilter.method POST .doFilter.getRequestURI /mvc/login
This is my login form too:
<form action="/mvc/login" method="post">
<input type="text" id="j_user" name="j_user">
<input type="text" id="j_password" name="j_password">
<input type="text" id="answer" name="answer">
<input type="submit">
</form>
This seems like right way to validate captcha. Most likly CSRF filter in intercepting the request and because of missing CSRF, it is redrecting you to error page (URL) that you see in the logs.
There was an unexpected error (type=Forbidden, status=403). Access Denied.
When I was trying to access URL from postman or from browser I am getting an error of There was an unexpected error (type=Forbidden, status=403). Access Denied.
1)Web Security Class:-
import javax.servlet.Filter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import com.photoapp.users.service.ApiUserService;
#Configuration
#EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
private Environment environment;
private ApiUserService apiUserService;
private BCryptPasswordEncoder bCryptPasswordEncoder;
#Autowired
public WebSecurity(Environment environment , ApiUserService apiUserService , BCryptPasswordEncoder bCryptPasswordEncoder) {
this.environment = environment;
this.apiUserService = apiUserService;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests().antMatchers("/**").hasIpAddress(environment.getProperty("gateway.ip"))
.and()
.addFilter(getAuthenticationFilter());
http.headers().frameOptions().disable();
}
private AuthenticationFilter getAuthenticationFilter() throws Exception {
// TODO Auto-generated method stub
AuthenticationFilter authenticationFilter = new AuthenticationFilter();
authenticationFilter.setAuthenticationManager(authenticationManager());
return authenticationFilter;
}
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(apiUserService).passwordEncoder(bCryptPasswordEncoder);
}
}
2)Authentication Filter Class:-
import java.io.IOException;
import java.util.ArrayList;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.photoapp.users.model.LoginRequestModel;
public class AuthenticationFilter extends UsernamePasswordAuthenticationFilter {
public Authentication attemptAuthentiation(HttpServletRequest req, HttpServletResponse res) throws AuthenticationException {
try {
LoginRequestModel creds = new ObjectMapper()
.readValue(req.getInputStream() , LoginRequestModel.class);
return getAuthenticationManager().authenticate(new UsernamePasswordAuthenticationToken (
creds.getEmail(),
creds.getPassword(),
new ArrayList<>()));
}
catch(IOException e) {
throw new RuntimeException(e);
}
}
protected void succeddfulAuthentication(HttpServletRequest req, HttpServletResponse res , FilterChain chain , Authentication auth) throws IOException , ServletException {
}
}
3)Controller Class:-
import org.modelmapper.ModelMapper;
import org.modelmapper.convention.MatchingStrategies;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.photoapp.users.dto.UserDto;
import com.photoapp.users.model.CreateUserRequestModel;
import com.photoapp.users.model.CreateUserResponseModel;
import com.photoapp.users.service.ApiUserService;
#RestController
#RequestMapping("/users")
public class UsersController {
#Autowired
private ApiUserService apiUserService;
#GetMapping("/status/check")
public String status() {
return "Working";
}
#PostMapping( consumes = {MediaType.APPLICATION_XML_VALUE , MediaType.APPLICATION_JSON_VALUE } ,
produces = {MediaType.APPLICATION_XML_VALUE , MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<CreateUserResponseModel> createUser(#RequestBody CreateUserRequestModel userDetails) {
ModelMapper modelMapper = new ModelMapper();
modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
UserDto userDto = modelMapper.map(userDetails, UserDto.class);
UserDto createdUser = apiUserService.createUser(userDto);
CreateUserResponseModel responseModel = modelMapper.map(createdUser , CreateUserResponseModel.class);
return ResponseEntity.status(HttpStatus.CREATED).body(responseModel);
}
}
4)Service Implementation Class:-
import java.util.ArrayList;
import java.util.UUID;
import org.modelmapper.ModelMapper;
import org.modelmapper.convention.MatchingStrategies;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import com.photoapp.users.dao.UserRepository;
import com.photoapp.users.dto.UserDto;
import com.photoapp.users.entity.UserEntity;
#Service
public class ApiUserServiceImpl implements ApiUserService{
UserRepository userRepository;
BCryptPasswordEncoder bCryptPasswordEncoder;
#Autowired
public ApiUserServiceImpl(UserRepository userRepository, BCryptPasswordEncoder bCryptPasswordEncoder) {
this.userRepository = userRepository;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
#Override
public UserDto createUser(UserDto userDetails) {
userDetails.setUserId(UUID.randomUUID().toString());
userDetails.setEncryptedPassword(bCryptPasswordEncoder.encode(userDetails.getPassword()));
ModelMapper modelMapper = new ModelMapper();
modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
UserEntity userEntity = modelMapper.map(userDetails, UserEntity.class);
userRepository.save(userEntity);
return userDetails;
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// TODO Auto-generated method stub
UserEntity userEntity = userRepository.findByEmail(username);
if(userEntity == null) throw new UsernameNotFoundException(username);
return new User(userEntity.getEmail() , userEntity.getEncryptedPassword() , true ,true ,true ,true , new ArrayList<>());
}
}
Can you check if gateway.ip property is set in your environment variavales? if its mac,try echo ${gateway.ip}.
Other point is why are you limiting to just one ip? any specific reason?
Also can you confirm if removing .hasIpAddress(environment.getProperty("gateway.ip")) works?
In my application i configure AuthenticationEntryPoint in spring boot security config because if any error occurred (even my custom 404 - item was not found) spring boot catched error and return 401 (in my case) but i remeber that some people told about 403. So i think that 403 could hide real error from you
Therefore you could try to catch error via configure exception handling
.exceptionHandling().authenticationEntryPoint(new AppAuthenticationEntryPoint())
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.headers().frameOptions().sameOrigin();
http.cors();
http.csrf().disable()
.authorizeRequests().antMatchers("/", "/callback", "/login**", "/webjars/**", "/error**").permitAll()
.and()
.authorizeRequests().antMatchers("/api/**").authenticated()
.and()
.authorizeRequests().antMatchers("/h2-console/**").permitAll()
.and()
.authorizeRequests().antMatchers("/swagger-ui.html").permitAll()
.and()
.authorizeRequests().antMatchers("/swagger-ui/**").permitAll()
.and()
.exceptionHandling().authenticationEntryPoint(new AppAuthenticationEntryPoint())
.and()
.logout().permitAll().logoutSuccessUrl("/");
}
#Bean
public PrincipalExtractor getPrincipalExtractor(){
return new KeyCloakUserInfoExtractorService();
}
#Autowired
private ResourceServerTokenServices resourceServerTokenServices;
}
My AuthenticationEntryPoint looks like:
#ControllerAdvice
public class AppAuthenticationEntryPoint implements AuthenticationEntryPoint{
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException auth) throws IOException, ServletException {
// 401
setResponseError(response, HttpServletResponse.SC_UNAUTHORIZED, "Authentication Failed");
}
#ExceptionHandler (value = {AccessDeniedException.class})
public void commence(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
// 403
setResponseError(response, HttpServletResponse.SC_FORBIDDEN, String.format("Access Denies: %s", accessDeniedException.getMessage()));
}
#ExceptionHandler (value = {NotFoundException.class})
public void commence(HttpServletRequest request, HttpServletResponse response, NotFoundException notFoundException) throws IOException {
// 404
setResponseError(response, HttpServletResponse.SC_NOT_FOUND, String.format("Not found: %s", notFoundException.getMessage()));
}
#ExceptionHandler (value = {Exception.class})
public void commence(HttpServletRequest request, HttpServletResponse response, Exception exception) throws IOException {
//logger.error(String.format("An error occurred during request: %s %s error message: %s",
//request.getMethod(), request.getRequestURL(), exception.getMessage()));
// 500
setResponseError(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, String.format("Internal Server Error: %s", exception.getMessage()));
}
private void setResponseError(HttpServletResponse response, int errorCode, String errorMessage) throws IOException{
response.setStatus(errorCode);
response.getWriter().write(errorMessage);
response.getWriter().flush();
response.getWriter().close();
}
//private final Logger logger = LoggerFactory.getLogger(this.getClass());
}
Hope it helps you to understand reason of 403
You must permit all of the swagger related URLs.
http.authorizeRequests().antMatchers(
"/swagger-ui.html/**",
"/webjars/springfox-swagger-ui/**",
"/swagger-resources/**",
"/swagger-ui/**",
"/v2/api-docs/**"
).permitAll();
I am using spring-boot 2.5.0 for a REST API and implemented OAuth using following classes. I am aware that in grant type 'client_credentials' refresh token is not returned. However, the android team I am working with is adamant about having refresh token in grant type 'client_credentials' .
So I am seeking a way to get refresh tokens in client_credential
Currently , as per the default, I am only getting refresh_token in grant_type password.
The classes I am using :
1) AuthorizationServerConfiguration
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.approval.UserApprovalHandler;
import org.springframework.security.oauth2.provider.token.TokenStore;
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
private static String REALM="MY_OAUTH_REALM";
#Autowired
private TokenStore tokenStore;
#Autowired
private UserApprovalHandler userApprovalHandler;
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
int accessTokenMinutesValidity = 60;
int refreshTokenMinutesValidity = 24 * 60;
clients.inMemory()
.withClient("my-trusted-client")
.authorizedGrantTypes("client_credentials", "refresh_token")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.scopes("read", "write", "trust")
.secret("{noop}secret")
.accessTokenValiditySeconds(60 * accessTokenMinutesValidity ).//Access token is only valid for 60 minutes.
refreshTokenValiditySeconds(60 * refreshTokenMinutesValidity);//Refresh token is only valid for 24 hours
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore).userApprovalHandler(userApprovalHandler)
.authenticationManager(authenticationManager);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer
.allowFormAuthenticationForClients()
.realm(REALM+"/client");
}
}
2) MethodSecurityConfig
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
import org.springframework.security.oauth2.provider.expression.OAuth2MethodSecurityExpressionHandler;
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
#Autowired
private OAuth2SecurityConfiguration securityConfig;
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new OAuth2MethodSecurityExpressionHandler();
}
}
3) OAuth2SecurityConfiguration
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.approval.TokenApprovalStore;
import org.springframework.security.oauth2.provider.approval.TokenStoreUserApprovalHandler;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
#SuppressWarnings("deprecation")
#Configuration
#EnableWebSecurity
public class OAuth2SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private ClientDetailsService clientDetailsService;
#Autowired
private DataSource dataSource;
#Primary
#Bean
public DataSource customDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("oracle.jdbc.driver.OracleDriver");
dataSource.setUrl("<connection string>");
dataSource.setUsername("<username>");
dataSource.setPassword("<password>");
return dataSource;
}
#Bean
#ConfigurationProperties("spring.datasource")
public DataSource ds() {
//return DataSourceBuilder.create().build();
return customDataSource();
}
//USERS FROM DATABASE
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception
{
PasswordEncoder encoder = NoOpPasswordEncoder.getInstance();
BCryptPasswordEncoder enc;
auth.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery("select USERNAME, ENC_PASSWD as PASSWORD, IS_ACTIVE AS ENABLED FROM USER_MSTR WHERE USERNAME=?")
.authoritiesByUsernameQuery("select USERNAME, 'ROLE_CLIENT' as ROLE from USER_MSTR where USERNAME=?")
.passwordEncoder(NoOpPasswordEncoder.getInstance())
;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.anonymous().disable()
.authorizeRequests()
.antMatchers("/oauth/token").permitAll();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
#Bean
#Autowired
public TokenStoreUserApprovalHandler userApprovalHandler(TokenStore tokenStore){
TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler();
handler.setTokenStore(tokenStore);
handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));
handler.setClientDetailsService(clientDetailsService);
return handler;
}
#Bean
#Autowired
public ApprovalStore approvalStore(TokenStore tokenStore) throws Exception {
TokenApprovalStore store = new TokenApprovalStore();
store.setTokenStore(tokenStore);
return store;
}
}
4) ResourceServerConfiguration
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler;
#Configuration
#EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
private static final String RESOURCE_ID = "my_rest_api";
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID).stateless(false);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.
anonymous().disable()
.requestMatchers()
.antMatchers("/category_mstr/**", "/equipment/**", "/param_mstr/**", "/chklist_txn/**", "/settings/**", "/user/**")
.and().authorizeRequests()
.antMatchers("/category_mstr/**", "/equipment/**", "/param_mstr/**", "/chklist_txn/**", "/settings/**", "/user/**")
.access("hasRole('ROLE_CLIENT')")
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
}
currently with grant_type = password, I am getting response as
{
"access_token": "25ca1254-4460-44bd-94c3-367f4b21e3b0",
"token_type": "bearer",
"refresh_token": "ef64adad-3772-4e0c-ae30-50358cdfffe1",
"expires_in": 3599,
"scope": "read trust write"
}
and with grant_type = client_credentials, I am getting response as
{
"access_token": "8c2bcdac-0a6b-4262-8664-75f1e0dc8351",
"token_type": "bearer",
"expires_in": 3599,
"scope": "read trust write"
}
How do I get response with refresh_token in above ??
I have tried extending ClientCredentialsAccessTokenProvider as follows
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsAccessTokenProvider;
public class CCAccessTokenProvider extends ClientCredentialsAccessTokenProvider {
#Override
public boolean supportsRefresh(OAuth2ProtectedResourceDetails resource) {
return true;
}
}
but I don't know where to fit it in my current architecture
--EDIT
I am seeking a solution to this critical issue and the other thread doesn't address it .