优化资源服务器拦截与Spring Security拦截优先级问题,资源服务拦截器优先级高于Spring Security,限制资源服务器拦截作用范围

This commit is contained in:
zhuyijun 2022-09-18 23:50:30 +08:00
parent e8dbdd18e2
commit 858dbd3abf
8 changed files with 168 additions and 21 deletions

View File

@ -0,0 +1,33 @@
package cn.zyjblogs.starter.oauth.handler;
import cn.zyjblogs.starter.common.entity.response.HttpCode;
import cn.zyjblogs.starter.common.entity.response.ResponseResult;
import org.codehaus.jackson.map.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author zhuyijun
*/
public class ResourceAccessDeniedHandler implements AccessDeniedHandler {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
private static final Logger log = LoggerFactory.getLogger(ResourceAccessDeniedHandler.class);
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
log.error("禁止访问 {}", e.getMessage());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
OBJECT_MAPPER.writeValue(response.getOutputStream(),
ResponseResult.error(HttpCode.FORBIDDEN, "无权访问该资源"));
}
}

View File

@ -0,0 +1,32 @@
package cn.zyjblogs.starter.oauth.handler;
import cn.zyjblogs.starter.common.entity.response.HttpCode;
import cn.zyjblogs.starter.common.entity.response.ResponseResult;
import org.codehaus.jackson.map.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author zhuyijun
*/
public class ResourceAuthenticationEntryPoint implements AuthenticationEntryPoint {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
private static final Logger log = LoggerFactory.getLogger(ResourceAuthenticationEntryPoint.class);
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
log.error("认证失败 {}", e.getMessage());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
OBJECT_MAPPER.writeValue(response.getOutputStream(),
ResponseResult.error(HttpCode.UNAUTHORIZED, "认证失败,token无效!"));
}
}

View File

@ -1,5 +1,7 @@
package cn.zyjblogs.starter.oauth.resource; package cn.zyjblogs.starter.oauth.resource;
import cn.zyjblogs.starter.oauth.handler.ResourceAccessDeniedHandler;
import cn.zyjblogs.starter.oauth.handler.ResourceAuthenticationEntryPoint;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.cloud.context.config.annotation.RefreshScope;
@ -30,7 +32,9 @@ public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
resources.resourceId(resourceId) resources.resourceId(resourceId)
// 验证令牌的服务 // 验证令牌的服务
.tokenStore(tokenStore) .tokenStore(tokenStore)
.stateless(true); .stateless(true)
.accessDeniedHandler(new ResourceAccessDeniedHandler())
.authenticationEntryPoint(new ResourceAuthenticationEntryPoint());
} }
@Override @Override
@ -42,7 +46,8 @@ public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
.anyRequest() .anyRequest()
.authenticated() .authenticated()
.and() .and()
.csrf().disable(); .csrf().disable()
.httpBasic();
} }
} }

View File

@ -0,0 +1,33 @@
package cn.zyjblogs.oauth.config.handler;
import cn.zyjblogs.starter.common.entity.response.HttpCode;
import cn.zyjblogs.starter.common.entity.response.ResponseResult;
import org.codehaus.jackson.map.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author zhuyijun
*/
public class ResourceAccessDeniedHandler implements AccessDeniedHandler {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
private static final Logger log = LoggerFactory.getLogger(ResourceAccessDeniedHandler.class);
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
log.error("禁止访问 {}", e.getMessage());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
OBJECT_MAPPER.writeValue(response.getOutputStream(),
ResponseResult.error(HttpCode.FORBIDDEN, "无权访问Oauth资源"));
}
}

View File

@ -0,0 +1,32 @@
package cn.zyjblogs.oauth.config.handler;
import cn.zyjblogs.starter.common.entity.response.HttpCode;
import cn.zyjblogs.starter.common.entity.response.ResponseResult;
import org.codehaus.jackson.map.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author zhuyijun
*/
public class ResourceAuthenticationEntryPoint implements AuthenticationEntryPoint {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
private static final Logger log = LoggerFactory.getLogger(ResourceAuthenticationEntryPoint.class);
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
log.error("认证失败 {}", e.getMessage());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
OBJECT_MAPPER.writeValue(response.getOutputStream(),
ResponseResult.error(HttpCode.UNAUTHORIZED, "认证失败!"));
}
}

View File

@ -1,12 +1,12 @@
package cn.zyjblogs.oauth.config.security; package cn.zyjblogs.oauth.config.security;
import cn.zyjblogs.oauth.config.handler.ResourceAccessDeniedHandler;
import cn.zyjblogs.oauth.config.handler.ResourceAuthenticationEntryPoint;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; 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.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer; import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
@ -16,7 +16,7 @@ import org.springframework.security.oauth2.provider.token.TokenStore;
@EnableResourceServer @EnableResourceServer
@RequiredArgsConstructor @RequiredArgsConstructor
@RefreshScope @RefreshScope
@Order(100) //@Order(50)
public class ResourceServerConfig extends ResourceServerConfigurerAdapter { public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Value("${spring.application.name}") @Value("${spring.application.name}")
private String resourceId; private String resourceId;
@ -27,22 +27,35 @@ public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
resources.resourceId(resourceId) resources.resourceId(resourceId)
// 验证令牌的服务 // 验证令牌的服务
.tokenStore(tokenStore) .tokenStore(tokenStore)
.stateless(true); .stateless(true)
.accessDeniedHandler(new ResourceAccessDeniedHandler())
.authenticationEntryPoint(new ResourceAuthenticationEntryPoint());
} }
/**
* 资源服务器拦截优先级高于Spring Scurity限制资源服务器作用范围
*
* @param http
* @return void
* @author zhuyijun
* @date 2022/9/18 下午10:52
*/
@Override @Override
public void configure(HttpSecurity http) throws Exception { public void configure(HttpSecurity http) throws Exception {
http.sessionManagement() http
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) .csrf()
.and() .disable()
//限制资源服务器作用范围为 "/user/**", "/demo/**"
.requestMatchers().antMatchers("/user/**", "/demo/**").and()
.formLogin().and()
.authorizeRequests() .authorizeRequests()
.antMatchers("/user/login", "/login", "/user/refresh/token")
.permitAll()
.antMatchers("/webjars/**", "/swagger-ui.html/**", "/swagger-resources/**", "/v2/api-docs/**") .antMatchers("/webjars/**", "/swagger-ui.html/**", "/swagger-resources/**", "/v2/api-docs/**")
.permitAll() .permitAll()
.anyRequest().authenticated() .antMatchers("/login", "/oauth/**", "/user/login", "/user/refresh/token").permitAll()
.and() //以下请求必须认证通过
.csrf().disable(); .anyRequest()
.authenticated().and()
.httpBasic();
} }
} }

View File

@ -4,7 +4,6 @@ import cn.zyjblogs.oauth.server.user.service.impl.OauthUserDetailsServiceImpl;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 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.builders.HttpSecurity;
@ -16,7 +15,7 @@ import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration @Configuration
@EnableWebSecurity @EnableWebSecurity
@RequiredArgsConstructor @RequiredArgsConstructor
@Order(1) //@Order(1)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter { public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
private final OauthUserDetailsServiceImpl userDetailsService; private final OauthUserDetailsServiceImpl userDetailsService;
@ -55,12 +54,11 @@ public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
http.csrf().disable(); http.csrf().disable();
//使HttpSecurity接收以"/login/","/oauth/"开头请求, 配置HttpSecurity不阻止swagger页面 //使HttpSecurity接收以"/login/","/oauth/"开头请求, 配置HttpSecurity不阻止swagger页面
http.authorizeRequests() http.authorizeRequests()
.antMatchers("/webjars/**", "/swagger-ui.html/**", "/swagger-resources/**", "/v2/api-docs/**") // .antMatchers("/webjars/**", "/swagger-ui.html/**", "/swagger-resources/**", "/v2/api-docs/**")
.permitAll() // .permitAll()
.antMatchers("/login", "/oauth/**", "/user/logout", "/user/login", "/user/refresh/token").permitAll() // .antMatchers("/user/logout", "/login", "/oauth/**", "/user/login", "/user/refresh/token").permitAll()
//以下请求必须认证通过 //以下请求必须认证通过
.anyRequest() .anyRequest().authenticated()
.authenticated()
.and() .and()
//允许表单登录 //允许表单登录
.formLogin() .formLogin()

View File

@ -27,6 +27,7 @@ spring:
logging: logging:
config: classpath:logback-spring.xml config: classpath:logback-spring.xml
#security: #security:
# oauth2: # oauth2:
# client: # client: