一、OAuth2授权机制
1.1 为什么需要OAuth2授权
参考OAuth 2.0 的一个简单解释 - 阮一峰的网络日志 (ruanyifeng.com)
1.2 OAuth2四种授权方式
参考OAuth 2.0 的四种方式 - 阮一峰的网络日志 (ruanyifeng.com)
- 授权码
- 隐藏式:适用于无后端服务器的第三方应用(纯前端页面)
- 密码式
- 凭证式:适用于没有前端的命令行应用
1.3 授权码模式的授权流程
- 用户访问和登录第三方网站时,点击微信、QQ、新浪等第三方登录按钮,发送https://b.com/oauth/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=CALLBACK_URL&scope=read请求到授权服务器;
- 授权服务器返回用户登录页面,用户登录后跳转到授权确认页面,用户同意授权后重定向到https://a.com/callback?code=AUTHORIZATION_CODE到前端地址栏中,这个重定向get请求会访问第三方应用的后端服务器;
- 第三方应用的后端服务器携带授权码、客户端id、客户端秘钥去授权服务器获取访问令牌;
- 授权服务器返回访问令牌到第三方应用的后端服务器
- 后端服务器用访问令牌去访问资源服务器获取资源,后端服器把获取到的资源返回给前端
二、Spring Cloud Security OAuth2
Spring Cloud Security OAuth2
是一个基于Spring Security实现了OAuth2授权机制的安全框架,框架主要涉及以下四个对象:
- 授权服务器
- 资源服务器
- 客户端:指第三方应用
- 资源所有者
在Spring Cloud Security包含security
和OAuth2
两个依赖,应用导入OAuth2
会自动导入security
依赖
2.1 授权服务器配置
2.1.1 配置依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
2.1.2 配置资源所有者认证
为第三方应用授权之前,资源所有者用户必须先登录授权服务器,所以要配置用户登录
配置用户登录的方法,同传统Spring Security
一样:
@Configuration
public class BaseSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("admin")
.password(new BCryptPasswordEncoder().encode("123456"))
.roles("admin")
.and()
.withUser("wexia")
.password(new BCryptPasswordEncoder().encode("123456"))
.roles("user");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().formLogin();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
用户信息储存在内存中,也可以实现UserDetailService
服务从数据库中获取用户信息
2.1.3 配置授权服务器
继承AuthorizationServerConfigurerAdapter
配置客户端信息、授权码储存方式、令牌生成及储存方式以及令牌端点的安全约束:
@Configuration
@EnableAuthorizationServer
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
@Autowired
private ClientDetailsService clientDetailsService;
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.checkTokenAccess("permitAll()")
.allowFormAuthenticationForClients();
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("weixia") //客户端ID
// 客户端密钥,储存时要加密储存
// 获取令牌或远程校验令牌时,客户端传入未加密的客户端密钥,系统会自动进行加密
.secret(new BCryptPasswordEncoder().encode("123"))
// 可选,设置能访问的资源ID
.resourceIds("res-1")
// 授权类型
.authorizedGrantTypes("authorization_code", "refresh_token")
// 授权范围
.scopes("all")
// 重定向地址
.redirectUris("http://localhost:8082/index.html");
}
/**
*
* 1、配置授权码储存方式
* 2. 配置令牌(储存方式、令牌刷新)
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authorizationCodeServices(authorizationCodeServices())
.tokenServices(authorizationServerTokenServices())
.pathMapping("/oauth/confirm_access", "/oauth/custom_confirm_access");
}
@Bean
public AuthorizationCodeServices authorizationCodeServices() {
return new InMemoryAuthorizationCodeServices();
}
@Bean
public AuthorizationServerTokenServices authorizationServerTokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenStore(tokenStore());
tokenServices.setSupportRefreshToken(true);
tokenServices.setClientDetailsService(clientDetailsService);
tokenServices.setAccessTokenValiditySeconds(60 * 60 * 2);
tokenServices.setRefreshTokenValiditySeconds(60 * 60 * 24 * 3);
return tokenServices;
}
@Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
}
- @EnableAuthorizationServer注解导入授权服务器的自动化配置
- AuthorizationServerSecurityConfigurer配置令牌端点的安全约束,通俗的讲,就是配置访问
oauth/token
等受限接口的行为(是否允许访问)。checkTokenAccess
方法配置是否允许资源服务器访问oauth/token
接口进行token鉴权;allowFormAuthenticationForClients
方法用于获取令牌时,允许客户端端信息通过请求体传递,如果不没配置只能通过basic认证传递客户端信息(allowFormAuthenticationForClients作用_树欲静而风不止的博客-CSDN博客) - ClientDetailsServiceConfigurer配置客户端信息,描述了客户端消息如何储存以及客户端ID、客户端密钥、客户端支持的授权类型、授权范围、重定向地址;
resourceIds
是可选方法,如果配置了资源ID那么客户端只能访问对应ID的资源服务器(资源服务器配置时要设置资源ID),如果不配置资源ID则可以访问任意资源服务器。第三方应用必须在授权服务器先进行备案,才能获取令牌 - AuthorizationServerEndpointsConfigurer配置授权码、访问令牌及请求转发。
authorizationCodeServices
方法配置授权码储存方式,框架内置内存和数据库两种储存方式,但授权码有效期短且用过就会失效,一般用内存储存方式就行;tokenServices
配置令牌服务,令牌服务提供了令牌存储方式、令牌有效期、是否刷新等配置;pathMapping
方法主要用于请求映射,请求A链接会直接重定向到B链接,借此可以实现自定义授权页面、错误页面
2.1.4 用户认证是怎么实现重定向的
授权服务器分别继承了AuthorizationServerConfigurerAdapter
和WebSecurityConfigurerAdapter
,相当于创建了2条filterChain,如下图所示:
AuthorizationServerConfigurerAdapter
创建的过滤器只拦截/oauth/token
等接口,过滤链中不包含用户认证过滤器,所以要继承WebSecurityConfigurerAdapter
来配置资源所有者认证
WebSecurityConfigurerAdapter
创建的调用链拦截/**
任意链接,提供了表单认证过滤器,但过滤器链不包含鉴权过滤器FilterSecurityInterceptor
- 当用户发送
http://localhost:8080/oauth/authorize?client_id=weixia&response_type=code&scope=all&redirect_uri=http://localhost:8082/index.html
,会进入WebSecurityConfigurerAdapter
创建的过滤器链,由于用户没有认证,系统会返回一个/login
登录页面; - 当用户成功登录后,会执行
UsernamePasswordAuthenticationFilter
的successfulAuthentication方法,该方法会重定向链接到用户原先要访问的地址 - 步骤2重定向发送的是一个get请求,请求
http://localhost:8080/oauth/authorize?client_id=weixia&response_type=code&scope=all&redirect_uri=http://localhost:8082/index.html
,内置的控制器AuthorizationEndpoint.authorize
会重定向地址到/oauth/confirm_access
返回一个授权确认页面 - 用户确认授权后,会发送post请求到
/oauth/authorize
,内置控制器校验成功后把生成的授权码附加到重定向地址中,进行重定向
2.2 资源服务器搭建
2.2.1 配置依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
2.2.2 配置授权服务器
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("res-1").tokenServices(remoteTokenServices());
}
@Bean
public RemoteTokenServices remoteTokenServices() {
RemoteTokenServices tokenServices = new RemoteTokenServices();
tokenServices.setCheckTokenEndpointUrl("http://localhost:8080/oauth/check_token");
tokenServices.setClientId("weixia");
tokenServices.setClientSecret("123");
return tokenServices;
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().anyRequest().authenticated();
}
}
- @EnableResourceServer注解导入资源服务器自动配置
- ResourceServerSecurityConfigurer主要配置资源ID以及令牌服务,令牌服务描述了如何鉴权。RemoteTokenServices是远程token鉴权服务,资源服务器接到第三方应用请求后,会传递客户端信息去访问授权服务器的
/oauth/check_token
接口,鉴定token是否合法 - HttpSecurity描述了资源服务器上那些接口需要鉴权
2.2.3 资源服务器创建的过滤器链
chain: any request, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@6704df84, org.springframework.security.web.context.SecurityContextPersistenceFilter@3214bad, org.springframework.security.web.header.HeaderWriterFilter@65d9e72a, org.springframework.security.web.authentication.logout.LogoutFilter@2755617b, org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter@6e8f2094, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@f95d64d, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@3ca17943, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@1412682c, org.springframework.security.web.session.SessionManagementFilter@27df5806, org.springframework.security.web.access.ExceptionTranslationFilter@13004dd8, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@4ce267cc]
- 资源服务器过滤器链中新增了
OAuth2AuthenticationProcessingFilter
,负载对请求中携带的token进行校验,如果校验成功会把用户信息封装成Authentication
放到security上下文SecurityContextHolder.getContext()
中,表示当前请求登录成功; - 此后流程同传统spring security一样,由
FilterSecurityInterceptor
进行鉴权,如何实现基于角色的控制或动态授权同之前一样
2.3 第三方应用
第三方应用使用postman进行测试:
-
用户在浏览器访问
http://localhost:8080/oauth/authorize?client_id=weixia&response_type=code&scope=all&redirect_uri=http://localhost:8082/index.html
,重定向到登录界面 -
用户输入登录帐号、密码,重定向到授权确认页面,用户同意授权后重定义到
http://localhost:8082/index.html?code=DGb0oO
-
postman发送post请求到
/oauth/token
,获取访问令牌 -
从步骤3获取access_token,向资源服务器发送请求
访问资源服务器时,access_token要放到请求头
Authorization
中,其值格式为Bearer
+token
三、客户端信息数据库储存
客户端信息储存及查询主要涉及的接口是ClientDetailsService
,其有两个内置实现类:
- InMemoryClientDetailsService
- JdbcClientDetailsService
从JdbcClientDetailsService
的查询及插入语句可以看出表名及表字段
private static final String BASE_FIND_STATEMENT = "select client_id, client_secret, resource_ids, scope, authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, refresh_token_validity, additional_information, autoapprove from oauth_client_details";
private static final String DEFAULT_INSERT_STATEMENT = "insert into oauth_client_details (client_secret, resource_ids, scope, authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, refresh_token_validity, additional_information, autoapprove, client_id) values (?,?,?,?,?,?,?,?,?,?,?)";
-
创建数据库表如下:
DROP TABLE IF EXISTS `oauth_client_details`; CREATE TABLE `oauth_client_details` ( `client_id` varchar(48) NOT NULL, `resource_ids` varchar(256) DEFAULT NULL, `client_secret` varchar(256) DEFAULT NULL, `scope` varchar(256) DEFAULT NULL, `authorized_grant_types` varchar(256) DEFAULT NULL, `web_server_redirect_uri` varchar(256) DEFAULT NULL, `authorities` varchar(256) DEFAULT NULL, `access_token_validity` int(11) DEFAULT NULL, `refresh_token_validity` int(11) DEFAULT NULL, `additional_information` varchar(4096) DEFAULT NULL, `autoapprove` varchar(256) DEFAULT NULL, PRIMARY KEY (`client_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
授权服务器添加数据源依赖:
<!-- 如果引入mybatis start可以省略JDBC依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency>
-
配置数据源
datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/security_demo?timeZoneServer=Asia/Shanghai&useSSL=false username: root password: password
-
配置
ClientDetailsService
实例@Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.withClientDetails(getClientDetailsService()); } @Bean public JdbcClientDetailsService getClientDetailsService() { JdbcClientDetailsService jdbcClientDetailsService = new JdbcClientDetailsService(dataSource); jdbcClientDetailsService.setPasswordEncoder(passwordEncoder); return jdbcClientDetailsService; }
测试前需要进行客户端信息注册,可以在控制器中调用JdbcClientDetailsService.addClientDetails方法传入一个ClientDetails实例进行注册,系统内置一个ClientDetails接口的实现类BaseClientDetails
@Service
public class ClientServiceImpl implements ClientService {
@Autowired
private JdbcClientDetailsService jdbcClientDetailsService;
@Override
public ResultVO insertClientDetail(BaseClientDetails baseClientDetails) {
jdbcClientDetailsService.addClientDetails(baseClientDetails);
return new ResultVO(ResultStatus.OK, "success", baseClientDetails);
}
}
前端传递参数时,registeredRedirectUris属性的键值名为redirect_uri,不能直接传web_server_redirect_uri(addClientDetails的方法调用链会自动转换redirect_uri为web_server_redirect_uri储存到数据库中)
如果客户端信息储存时设置了令牌有效期,则authorizationServerTokenServices可以省略有效期配置:
tokenServices.setAccessTokenValiditySeconds(60 * 60 * 2);
tokenServices.setRefreshTokenValiditySeconds(60 * 60 * 24 * 3);
四、OAuth2使用JWT令牌
4.1 JwtTokenStore实例配置
第三节我们实现了客户端信息储存到数据库中,下面修改access token的储存方式,access token访问令牌的储存主要涉及TokenStore
接口,其内置五个实现类:
JwtTokenStore类同JWT令牌生成相关,其依赖TokenEnhancer接口,JwtTokenStore实例配置如下:
// 实例化JwtTokenStore,依赖JwtAccessTokenConverter
// JwtAccessTokenConverter实现了TokenEnhancer接口
@Bean
public JwtTokenStore jwtTokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
// 设置jwt生成时的签名(即jwt密钥)
jwtAccessTokenConverter.setSigningKey("weixia");
return jwtAccessTokenConverter;
}
JwtAccessTokenConverter负责把默认的UUID token转换为jwt格式,同时支持用户信息和jwt相互转换
4.2 为jwt添加附加信息
JWT 默认生成的用户信息主要是用户角色、用户名等,如果我们希望在生成的 JWT 上面添加额外的信息则要自己实现TokenEnhancer接口接口:
@Component
public class CustomAdditionalInformation implements TokenEnhancer {
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) {
Map<String, Object> additionalInformation = oAuth2AccessToken.getAdditionalInformation();
additionalInformation.put("author", "weixia");
((DefaultOAuth2AccessToken) oAuth2AccessToken).setAdditionalInformation(additionalInformation);
return oAuth2AccessToken;
}
}
4.3 配置authorizationServerTokenServices,令TokenEnhancer生效
access token默认由DefaultTokenServices.createAccessToken方法生成,该方法最后会检查是否存在token增强器,如果存在则按装传入的增强器顺序依次执行enhance方法
@Bean
public AuthorizationServerTokenServices authorizationServerTokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenStore(jwtTokenStore());
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(jwtAccessTokenConverter(), customAdditionalInformation));
tokenServices.setTokenEnhancer(tokenEnhancerChain);
tokenServices.setSupportRefreshToken(true);
tokenServices.setClientDetailsService(getClientDetailsService());
//tokenServices.setAccessTokenValiditySeconds(60 * 60 * 2);
//tokenServices.setRefreshTokenValiditySeconds(60 * 60 * 24 * 3);
return tokenServices;
}
token增强链的顺序要保证JwtAccessTokenConverter位于附加信息增强器前面,生成的jwt如下所示,其中jti则是原始access token
去JSON Web Tokens - jwt.io查看JWT负荷部分解析后数据如下:
4.4 资源服务器配置,实现JWT认证
配置JwtTokenStore实例,修改ResourceServerSecurityConfigurer配置,改为用tokenStore鉴权
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("res-1").tokenStore(jwtTokenStore());
}
@Bean
public JwtTokenStore jwtTokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
jwtAccessTokenConverter.setSigningKey("weixia");
return jwtAccessTokenConverter;
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests().anyRequest().authenticated();
}
}
当第三方应用携带JWT令牌访问资源服务器时,过滤器OAuth2AuthenticationProcessingFilter调用认证管理器校验JWT合法性并解析JWT字符串转换为Authentication对象放到security上下文中
五、自定义授权界面
-
引入Thyemeleaf依赖,强烈建议clean-install-重启IDEA,避免灵异事件
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
-
配置Thyemeleaf
spring: thymeleaf: cache: false
-
编写自定义页面,放到类路径下的templates中
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>授权</title> </head> <!-- 省略样式 --> <body style="margin: 0px"> <div class="title"> <div class="title-right">OAuth2 自定义授权页面</div> </div> <div class="container"> <p>昵称,账号</p> 授权后表明你已同意 <form method="post" action="/oauth/authorize"> <input type="hidden" name="user_oauth_approval" value="true"> <input type="hidden" name="scope.all" value="true"> <button class="btn" name="authorize" value="Authorize" type="submit"> 同意/授权</button> </form> </div> </body> </html>
表单参数user_oauth_approval、scope.all、authorize必传,请求接口为
/oauth/authorize
-
请求映射配置,把
/oauth/confirm_access
重定向到自定义接口@Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.authorizationCodeServices(authorizationCodeServices()) .tokenServices(authorizationServerTokenServices()) .pathMapping("/oauth/confirm_access", "/oauth/custom_confirm_access"); }
-
请求接口控制器
@Controller public class ConfirController { @RequestMapping("/oauth/custom_confirm_access") public String getAccessConfirmation(Map<String, Object> model, HttpServletRequest request) throws Exception { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); model.put("username", authentication.getName()); return "auth"; } }
六、资源服务器认证异常和授权异常
6.1 认证异常(token无效异常)
客户端访问资源服务器时,OAuth2AuthenticationProcessingFilter过滤器检查请求头是否携带Authorization
并按照Bearer token
格式提取token,如果token值为空则直接执行下个过滤器,不为空则会校验token
如果token校验结果为无效令牌,会抛出OAuth2Exception异常并由authenticationEntryPoint.commence方法处理,该方法把异常信息输出并直接中断过滤器链,我们可以实现AuthenticationEntryPoint接口返回统一异常信息
@Component
public class TokenAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Autowired
private ObjectMapper objectMapper;
@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
httpServletResponse.setCharacterEncoding("utf-8");
httpServletResponse.setContentType("application/json");
ResultVO result = new ResultVO(ResultStatus.NO, "token校验失败", null);
objectMapper.writeValue(httpServletResponse.getOutputStream(), result);
}
}
通过配置ResourceServerSecurityConfigurer配置异常处理器:
@Autowired
private TokenAuthenticationEntryPoint tokenAuthenticationEntryPoint;
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("res-1")
.tokenStore(jwtTokenStore())
.authenticationEntryPoint(tokenAuthenticationEntryPoint);
}
private AccessDeniedHandler accessDeniedHandler = new OAuth2AccessDeniedHandler();
ResourceServerSecurityConfigurer还有一个访问拒绝处理器,不知何种情景下会调用,待完善...
注意:
如果配置路径授权忽略,比如antMatchers("/play").permitAll(),请求时如果携带了token而token无效,同样会抛出token认证异常
6.2 授权异常
6.2.1 未认证用户访问受限资源异常
针对访问资源服务器时未携带token,但资源必须登录后才能访问而抛出的异常。该异常处理器同传统
Spring Security配置一样:
@Component
public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Autowired
private ObjectMapper objectMapper;
@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
httpServletResponse.setCharacterEncoding("utf-8");
httpServletResponse.setContentType("application/json");
ResultVO result = new ResultVO(ResultStatus.NO, "该资源需要登录访问", null);
objectMapper.writeValue(httpServletResponse.getOutputStream(), result);
}
}
6.2.1 已认证用户权限不足异常
@Component
public class MyAccessDeniedHandler implements AccessDeniedHandler {
@Autowired
private ObjectMapper objectMapper;
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
httpServletResponse.setCharacterEncoding("utf-8");
httpServletResponse.setContentType("application/json");
ResultVO result = new ResultVO(ResultStatus.NO, "权限不足!", null);
objectMapper.writeValue(httpServletResponse.getOutputStream(), result);
}
}
6.2.2 配置授权异常处理器
@Autowired
private MyAuthenticationEntryPoint myAuthenticationEntryPoint;
@Autowired
private MyAccessDeniedHandler myAccessDeniedHandler;
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.antMatchers("/world").hasRole("user")
.antMatchers("/play").permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint(myAuthenticationEntryPoint)
.accessDeniedHandler(myAccessDeniedHandler);
}
@Bean
public ObjectMapper objectMapper() {
return new ObjectMapper();
}
七、其它事项
7.1 资源服务器全局资源忽略
资源服务器配置一般继承自ResourceServerConfigurerAdapter,ResourceServerConfigurerAdapter暂时未找到设置全局忽略的选项,如果通过ResourceServerConfigurerAdapter中的HttpSecurity配置请求忽略路径,请求依然会走一遍过滤器链。
如果再添加一份配置继承WebSecurityConfigurerAdapter,默认会再添加一个匹配任意路径的filterChain,我们可以让新增加的这个过滤器链匹配一个不用的地址,比如这样:
@Configuration
public class BaseSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/world");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/XX-XXXXXX/**").authorizeRequests().anyRequest().denyAll()
.and()
.csrf().disable();
}
}
这样既通过WebSecurity配置了全局资源忽略,又避免过滤器链冲突,但上面实现的不够优雅
7.2 授权服务器异常处理
实现太复杂,参考以下链接:
OAuth2.0实战:认证、资源服务异常自定义! - 知乎 (zhihu.com)
SpringSecurityOauth2系列学习(五):授权服务自定义异常处理 - 硝酸铜 - 博客园 (cnblogs.com)
标签:令牌,Spring,token,tokenServices,服务器,授权,Security,OAuth2.0,public From: https://www.cnblogs.com/weixia-blog/p/16657737.html