My Custom AccessDeniedHandler cannot called - spring

In my Spring Boot app,I have implemented a custom AccessDeniedHandler,but it never called,and the custom authenticationEntryPoint instead of it
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AuthenticationEntryPointImpl unauthorizedHandler;
#Autowired
private YzlAccessDeniedHandler yzlAccessDeniedHandler;
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception
{
httpSecurity
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
.antMatchers("/yzl/**").access("#yzlAccessImpl.hasPermit(request)")
.antMatchers(
HttpMethod.GET,
"/",
"/*.html",
"/**/*.html",
"/**/*.css",
"/**/*.js"
).permitAll()
.anyRequest().authenticated()
.and()
.headers().frameOptions().disable();
httpSecurity.exceptionHandling()
.authenticationEntryPoint(unauthorizedHandler)
.accessDeniedHandler(yzlAccessDeniedHandler);
}
}
the custom AccessDeniedHandler
#Component
public class YzlAccessDeniedHandler implements AccessDeniedHandler, Serializable
{
#Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
response.setStatus(403);
response.getWriter().write("Forbidden: access error" + accessDeniedException.getMessage());
}
}
the custom AuthenticationEntryPointImpl
#Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint, Serializable
{
private static final long serialVersionUID = -8970718410437077606L;
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e)
throws IOException
{
response.setStatus(401);
response.getWriter().write("Forbidden: Authentication failed");
}
}
I let the custom Method in the accessImpl return false always,and I expect the response is ""Forbidden: access error" while it was "Forbidden: Authentication failed".
#Component
public class YzlAccessImpl implements YzlAccess
{
#Override
public boolean hasPermit(HttpServletRequest request) {
return false;
}
}

Related

Spring Boot OAuth2 authentication with login form

i am new to Spring Boot and OAuth2 , i found ressources on github and trying to practice to understand more the architecture and flows, so i have the configuration as follow :
OAuth2Configuration.java
#Configuration
public class OAuth2Configuration {
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Autowired
private CustomAuthenticationEntryPoint customAuthenticationEntryPoint;
#Autowired
private CustomLogoutSuccessHandler customLogoutSuccessHandler;
#Override
public void configure(HttpSecurity http) throws Exception {
http
.exceptionHandling()
.authenticationEntryPoint(customAuthenticationEntryPoint)
.and()
.logout()
.logoutUrl("/oauth/logout")
.logoutSuccessHandler(customLogoutSuccessHandler)
.and()
.csrf()
.requireCsrfProtectionMatcher(new AntPathRequestMatcher("/oauth/authorize"))
.disable()
.headers()
.frameOptions().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/secure/**", "/person/**", "/product/**").authenticated()
.antMatchers(HttpMethod.GET, "/user/**").authenticated()
.antMatchers(HttpMethod.PUT, "/user/**").authenticated()
.antMatchers(HttpMethod.DELETE, "/user/**").authenticated()
.antMatchers(HttpMethod.POST, "/user").permitAll();
}
}
#Configuration
#EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter implements EnvironmentAware {
private static final String ENV_OAUTH = "authentication.oauth.";
private static final String PROP_CLIENTID = "clientid";
private static final String PROP_SECRET = "secret";
private static final String PROP_TOKEN_VALIDITY_SECONDS = "tokenValidityInSeconds";
private RelaxedPropertyResolver propertyResolver;
#Autowired
private DataSource dataSource;
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints
.tokenStore(tokenStore())
.authenticationManager(authenticationManager);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient(propertyResolver.getProperty(PROP_CLIENTID))
.scopes("read", "write")
.authorities(Authorities.ROLE_ADMIN.name(), Authorities.ROLE_USER.name())
.authorizedGrantTypes("password", "refresh_token")
.secret(propertyResolver.getProperty(PROP_SECRET))
.redirectUris("http://localhost:8080/login")
.accessTokenValiditySeconds(propertyResolver.getProperty(PROP_TOKEN_VALIDITY_SECONDS, Integer.class, 1800));
}
#Override
public void setEnvironment(Environment environment) {
this.propertyResolver = new RelaxedPropertyResolver(environment, ENV_OAUTH);
}
}
}
SecurityConfiguration.java
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Bean
public PasswordEncoder passwordEncoder() {
// Define the type of encode
return new BCryptPasswordEncoder();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
//.antMatchers("/h2console/**")
.antMatchers("/register")
.antMatchers("/activate")
.antMatchers("/lostpassword")
.antMatchers("/resetpassword")
//.antMatchers("/hello")
.antMatchers("/person")
.antMatchers("/product");
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true)
private static class GlobalSecurityConfiguration extends GlobalMethodSecurityConfiguration {
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new OAuth2MethodSecurityExpressionHandler();
}
}
}
CustomAuthenticationEntryPoint.java
#Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
private final Logger log = LoggerFactory.getLogger(CustomAuthenticationEntryPoint.class);
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException ae) throws IOException, ServletException {
log.info("Pre-authenticated entry point called. Rejecting access");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Access Denied");
}
}
what i want to achieve is authenticate users using loging form on browser in order to access protected ressources , but i don't know how in this configuration.
example :
when i access to /product , it shows all products cos it's not secured , but /product/3 for example is protected so it shows a blank webpage with error access denied , i want to show loging form.
when

Spring security bypass if has different Authorization header

I have implemented spring security with jwt and is working fine.
#Configuration
#Component
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomJWTProvider jwtTokenProvider;
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic().disable()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/health").permitAll()
.anyRequest().authenticated()
.and()
.apply(new CustomJwtConfigurer(jwtTokenProvider));
}
#Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
}
}
But i have a typical use case where i get jwt token or 64bit fixed length token in Authorization header.
If it is JWT token, spring security should work as is and if it is not, the control should be passed to some method (where i have my logic to validate that token)
My filter looks like
#Component
public class CustomFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterchain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String authToken = httpRequest.getHeader("Authorization");
if(myCustomValidator(authToken)){
//if this is true it should skip spring security jwt token verification
//which is in configure phase
}else{
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.sendError(403, "Access denied");
}
System.out.println("authToken="+authToken);
filterchain.doFilter(request, response);
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {}
#Override
public void destroy() {}
}
How/Where can i add this filter to bypass spring security, or any other mechanism to skip spring security based on authorization header?
You can use OncePerRequestFilter. You need to create a class and extend this class where you override its method(doFilterInternal) and put your logic there.
You can configure that filter like below.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class YourClass extends WebSecurityConfigurerAdapter {
#Autowired
private YourFilter authFilter;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterAt(authFilter, UsernamePasswordAuthenticationFilter.class);
}
}

Spring security: My Authorization filter authorizes my request even tho the URL is permited

In my security configuration class i have permitted the request to the welcome url and any other url which follows the "welcome/**" format.
this is my securityconfiguration class:
#EnableGlobalMethodSecurity(prePostEnabled = true)
//#Configuration
#EnableWebSecurity
public class JwtSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
private final CustomerDetailsService customerDetailsService;
#Autowired
private JwtAuthenticationEntryPoint unauthorizedHandler;
#Autowired
public JwtSecurityConfiguration(CustomerDetailsService customerDetailsService) {
this.customerDetailsService = customerDetailsService;
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(customerDetailsService)
.passwordEncoder(passwordEncoderBean());
}
#Bean
public PasswordEncoder passwordEncoderBean() {
return new BCryptPasswordEncoder();
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("**/resources/static/**")
.and()
.ignoring()
.antMatchers(
HttpMethod.GET,
"/",
"/*.html",
"/favicon.ico",
"/**/*.html",
"/**/*.css",
"/**/*.js",
"/index_assets/**"
);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/welcome/login").permitAll()
.antMatchers("/welcome").permitAll()
.antMatchers("/welcome/signup").permitAll()
.antMatchers("admin/rest/**").authenticated()
.and()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler)
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
//http.addFilterBefore(new JWTAuthenticationFilter(authenticationManager()), UsernamePasswordAuthenticationFilter.class);
http.addFilterBefore(new JWTAuthorizationFilter(authenticationManager(),customerDetailsService),UsernamePasswordAuthenticationFilter.class);
// disable page caching
http
.headers()
.frameOptions().sameOrigin() // required to set for H2 else H2 Console will be blank.
.cacheControl();
//http.headers().cacheControl();
}
}
but I noticed that in my JWTAuthorizationFilter.class the doFilterInternal() method picks up this URL
public class JWTAuthorizationFilter extends OncePerRequestFilter {
private final CustomerDetailsService customerDetailsService;
#Autowired
DefaultCookieService defaultCookieService;
public JWTAuthorizationFilter(AuthenticationManager authenticationManager, CustomerDetailsService customerDetailsService) {
// super(authenticationManager);
this.customerDetailsService = customerDetailsService;
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
String header = request.getHeader(HEADER);
if(Objects.isNull(header) || !header.startsWith(TOKEN_PREFIX)){
return;
}
UsernamePasswordAuthenticationToken usernamePasswordAuth = getAuthenticationToken(request);
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuth);
chain.doFilter(request,response);
}
private UsernamePasswordAuthenticationToken getAuthenticationToken(HttpServletRequest request){
String token = request.getHeader(HEADER);
if(Objects.isNull(token)) return null;
String username = Jwts.parser().setSigningKey(SECRET)
.parseClaimsJws(token.replace(TOKEN_PREFIX,""))
.getBody()
.getSubject();
UserDetails userDetails = customerDetailsService.loadUserByUsername(username);
return username != null ? new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()) : null;
}
}
What is the cause of this ?
Filter is suppose to pick up each and every request. It doesn't matter if that you have permitted or not in security configuration.
You have got two options:
If you don't want welcome/** to go through the filter you add it to web ignore
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("**/resources/static/**")
.and()
.ignoring()
.antMatchers(
HttpMethod.GET,
"/",
"/*.html",
"/favicon.ico",
"/**/*.html",
"/**/*.css",
"/**/*.js",
"/index_assets/**",
"/welcome/**"
);
}
But note, it will skip all filters and you may not want that.
In doFilterInternal method skip it when you find welcome/** pattern.

spring security oauth2 manipulate request url before redirect

I have a Vaadin application that is secured using spring security OAuth2. This works fine except for the occasional PUSH or HEARTBEAT endpoint being used to request first and thus triggering the auth process and the user ends up on the wrong page (These endpoints should not be visited directly by the user).
A simple but unsecure fix is to permitAll() on these endpoints. However as this poses a threat I need to close this hole up.
To do this I would like to parse and potentially edit the request url before redirecting to it at successfull auth. How would I go about doing this?
I would guess I need to add a filter somewhere in the chain to intercept the request and edit it. But I'm not sure where.
Here is my client:
#Configuration
#EnableOAuth2Sso
public class OAuthConfig extends WebSecurityConfigurerAdapter
{
#Override
protected void configure(HttpSecurity http) throws Exception
{
http.csrf().disable()
.authorizeRequests()
.antMatchers("/login**").permitAll()
.antMatchers("/vaadinServlet/PUSH/**").permitAll() //todo fix this hole
.antMatchers("/vaadinServlet/HEARTBEAT/**").permitAll() //todo fix this hole
.anyRequest().authenticated()
.and()
.logout()
.logoutSuccessUrl("/")
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"));
}
#Override
public void configure(WebSecurity web) throws Exception
{
web.ignoring().antMatchers("/css/*").antMatchers("/VAADIN/**"); // Static resources are ignored
}
}
And the server:
#Configuration
#EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter
{
//jwt token stuff & my own client/auth providers. Should not be important.
...
}
server login form:
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
{
#Autowired
private RestAuthenticationProvider authenticationProvider;
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception
{
auth.authenticationProvider(authenticationProvider);
}
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/forgetPassword*").permitAll()
.antMatchers(HttpMethod.POST,"/user/resetPassword*").permitAll()
.antMatchers(HttpMethod.GET,"/user/changePassword*").permitAll()
.antMatchers("/user/updatePassword*", "/user/savePassword*", "/updatePassword*")
.hasAnyAuthority("CHANGE_PASSWORD_PRIVILEGE","ROLE_USER")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.csrf().csrfTokenRepository(csrfTokenRepository());
}
private CsrfTokenRepository csrfTokenRepository()
{
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
}
Just add some implementation with your project
1: create Authentication Failure handler
#Component
public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
#Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
System.out.print("here failure");
String s=request.getParameter("username");
setDefaultFailureUrl("/login?error&username="+s);
super.onAuthenticationFailure(request,response,exception);
}
}
2: Authentication Success Handler
#Component
public class CustomAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest request , HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
/* custom Block
Do any thing here
*/
setDefaultTargetUrl("/home/");
super.onAuthenticationSuccess(request,response,authentication);
}
}
3: access request entry point
#Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
System.out.print("Unauthorized Access");
httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
}
Implement the components as per your requirement.

permitAll() requires Authentication

I'm having a go at developing a REST application with Spring and using JWT for authentication.
At the moment, what I'm trying to achieve is:
GET /api/subjects/* should be accessible to all users.
POST /api/subjects/* should only accessible to admin users.
The issue is that for both cases, the JWT filter gets invoked and I get an error response stating the JWT token is missing.
I've implemented my WebSecurityConfig as follows, including a JWT filter to replace the BasicAuthenticationFilter:
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
JWTAuthenticationEntryPoint authenticationEntryPoint;
#Autowired
JWTAuthenticationProvider jwtAuthenticationProvider;
#Override
public void configure(WebSecurity web) throws Exception {
//web.ignoring().antMatchers(HttpMethod.GET,"/api/subjects/*");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/api/subjects/*").permitAll()
.antMatchers(HttpMethod.POST, "/api/subjects/*").hasRole(Profile.Role.ADMIN.toString())
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterAt(authenticationTokenFilter(), BasicAuthenticationFilter.class)
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);
}
public JWTAuthenticationFilter authenticationTokenFilter() {
return new JWTAuthenticationFilter(authenticationManager(), authenticationEntryPoint);
}
public ProviderManager authenticationManager() {
return new ProviderManager(new ArrayList<AuthenticationProvider>(Arrays.asList(jwtAuthenticationProvider)));
}
}
My implementation of JWTAuthenticationFilter is based on the implementation of BasicAuthenticationFilter:
public class JWTAuthenticationFilter extends OncePerRequestFilter {
private static final String JWT_TOKEN_START = "JWT ";
private AuthenticationManager authenticationManager;
private AuthenticationEntryPoint authenticationEntryPoint;
public JWTAuthenticationFilter(AuthenticationManager authenticationManager, AuthenticationEntryPoint authenticationEntryPoint) {
Assert.notNull(authenticationManager, "Authentication Manager must not be null");
Assert.notNull(authenticationEntryPoint, "Authentication Entry point must not be null");
this.authenticationManager = authenticationManager;
this.authenticationEntryPoint = authenticationEntryPoint;
}
#Override
protected void doFilterInternal(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
FilterChain filterChain) throws ServletException, IOException {
String header = httpServletRequest.getHeader("Authorization");
if (header == null || !header.startsWith(JWT_TOKEN_START)) {
throw new IllegalStateException("Header does not contain: \"Authorization\":\"JWT <token>\". Value: "+header);
}
try {
String jwt = header.substring(JWT_TOKEN_START.length()).trim().replace("<", "").replace(">", "");
JWTAuthenticationToken jwtAuthenticationToken = new JWTAuthenticationToken(jwt);
this.authenticationManager.authenticate(jwtAuthenticationToken);
filterChain.doFilter(httpServletRequest, httpServletResponse);
} catch (AuthenticationException auth) {
SecurityContextHolder.clearContext();
this.authenticationEntryPoint.commence(httpServletRequest, httpServletResponse, auth);
}
}
}
What is causing this issue?

Resources