SpringSecurity+Oauth2(搭建资源服务)

前言 之前写过使用springsecurity搭建认证服务SpringSecurity+Oauth2:密码授权模式获取Token(MongoDB+JWT),这篇则写关于如何搭建资源服务,只有验证通过的token才能访问。
操作 1、配置Pom.xml引用spring-cloud-securityoauth2的jar包
2、配置主类@EnableResourceServer注解,开启资源服务
3、创建JWTTokenStoreConfig类,配置和解析token
4、创建ResourceServerConfiguration类配置访问权限以及自定义异常
5、自定义springsecurity异常信息(注意:认证和资源服务的自定义异常是统一的没有区别,下面会说明)
1、配置Pom.xml引用spring-cloud-securityoauth2的jar包

org.springframework.cloud spring-cloud-starter-oauth2 2.2.5.RELEASE org.springframework.cloud spring-cloud-security 2.2.5.RELEASE

2、配置主类@EnableResourceServer注解,开启资源服务
@SpringBootApplication // 资源保护服务 @EnableResourceServer // 服务发现 @EnableDiscoveryClient @EnableFeignClients @RefreshScope public class Xxxx { @Bean public BCryptPasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }public static void main(String[] args) { SpringApplication.run(Xxxx.class, args); } }

3、创建JWTTokenStoreConfig类,配置和解析token
@Configuration public class JWTTokenStoreConfig {@Autowired private ServiceConfig serviceConfig; //JWT @Bean public TokenStore tokenStore() { return new JwtTokenStore(jwtAccessTokenConverter()); }//JWT @Bean @Primary public DefaultTokenServices tokenServices() { DefaultTokenServices defaultTokenServices = new DefaultTokenServices(); defaultTokenServices.setTokenStore(tokenStore()); defaultTokenServices.setSupportRefreshToken(true); return defaultTokenServices; }//JWT // 从配置文件中获取jwt key,然后自己解析token是否有效, @Bean public JwtAccessTokenConverter jwtAccessTokenConverter() { JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); converter.setSigningKey(serviceConfig.getJwtSigningKey()); return converter; }}

4、创建ResourceServerConfiguration类配置访问权限以及自定义异常
@Configuration public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter{@Override public void configure(ResourceServerSecurityConfigurer resources) {OAuth2AuthenticationEntryPoint authenticationEntryPoint = new OAuth2AuthenticationEntryPoint(); authenticationEntryPoint.setExceptionTranslator(new CustomOAuthWebResponseExceptionTranslator()); resources.authenticationEntryPoint(authenticationEntryPoint); OAuth2AccessDeniedHandler oAuth2AccessDeniedHandler = new OAuth2AccessDeniedHandler(); oAuth2AccessDeniedHandler.setExceptionTranslator(new CustomOAuthWebResponseExceptionTranslator()); resources.accessDeniedHandler(oAuth2AccessDeniedHandler); }@Override public void configure(HttpSecurity http) throws Exception { http.authorizeRequests() // 放开/sendRegisterEmailMessage,不需要校验token .antMatchers("/websocket/**").permitAll() .antMatchers("/info/**").permitAll() .anyRequest().authenticated(); } }

5、自定义springsecurity异常信息
创建CustomOAuthException
@JsonSerialize(using = CustomOAuthExceptionSerializer.class) public class CustomOAuthException extends OAuth2Exception { private static Logger log = LoggerFactory.getLogger(CustomOAuthException.class); private String oAuth2ErrorCode; private int httpErrorCode; public CustomOAuthException(String msg, String oAuth2ErrorCode, int httpErrorCode) { super(msg); this.oAuth2ErrorCode = oAuth2ErrorCode; this.httpErrorCode = httpErrorCode; }}

【SpringSecurity+Oauth2(搭建资源服务)】创建CustomOAuthExceptionSerializer
public class CustomOAuthExceptionSerializer extends StdSerializer { private static Logger log = LoggerFactory.getLogger(CustomOAuthExceptionSerializer.class); protected CustomOAuthExceptionSerializer() { super(CustomOAuthException.class); }@Override public void serialize(CustomOAuthException e, JsonGenerator generator, SerializerProvider serializerProvider) throws IOException { generator.writeStartObject(); HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); generator.writeObjectField("error",e.getOAuth2ErrorCode()); generator.writeObjectField("status", e.getHttpErrorCode()); generator.writeObjectField("message", e.getMessage()); generator.writeObjectField("path", request.getServletPath()); generator.writeObjectField("timestamp", (new Date()).getTime()); if (e.getAdditionalInformation() != null) { for (Map.Entry entry : e.getAdditionalInformation().entrySet()) { String key = entry.getKey(); String add = entry.getValue(); generator.writeObjectField(key, add); } } generator.writeEndObject(); } }

创建CustomOAuthWebResponseExceptionTranslator
@Component public class CustomOAuthWebResponseExceptionTranslator extends DefaultWebResponseExceptionTranslator { private ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer(); private static Logger log = LoggerFactory.getLogger(CustomOAuthWebResponseExceptionTranslator.class); @Override public ResponseEntity translate(Exception e) throws Exception {ResponseEntity translate = super.translate(e); OAuth2Exception body = translate.getBody(); CustomOAuthException customOauthException = new CustomOAuthException(body.getMessage(),body.getOAuth2ErrorCode(),body.getHttpErrorCode()); ResponseEntity response = new ResponseEntity<>(customOauthException, translate.getHeaders(), translate.getStatusCode()); return response; } }

最后如果用户名或密码错误则返回下面的错误信息:
{ timestamp: 1658149719489, path: '/oauth/token', error: 'invalid_request', message: '用户名或密码错误' status: 400 }

如果token错误则返回下面的错误信息:
{ timestamp: 1658149719489, path: '/oauth/token', error: 'invalid_request', message: 'token错误或过期' status: 400 }

如果refresh_token错误则返回下面的错误信息:
{ timestamp: 1658149719489, path: '/oauth/token', error: 'invalid_request', message: 'refresh_token错误或过期' status: 400 }

资源服务的目录结构
SpringSecurity+Oauth2(搭建资源服务)
文章图片

认证服务的目录结构
SpringSecurity+Oauth2(搭建资源服务)
文章图片

总结 1、springsecurity 自带的异常信息不符合自己的需求的时候就要自定义返回错误信息
2、错误信息返回的错误码如果是401那么照样返回401,如果觉得401不太好可以自行设置
引用 SpringBoot Security的自定义异常

    推荐阅读