前言
之前写过使用springsecurity搭建认证服务SpringSecurity+Oauth2:密码授权模式获取Token(MongoDB+JWT),这篇则写关于如何搭建资源服务,只有验证通过的token才能访问。
操作
1、配置Pom.xml引用spring-cloud-security
和oauth2
的jar包
2、配置主类@EnableResourceServer
注解,开启资源服务
3、创建JWTTokenStoreConfig
类,配置和解析token
4、创建ResourceServerConfiguration
类配置访问权限以及自定义异常
5、自定义springsecurity
异常信息(注意:认证和资源服务的自定义异常是统一的没有区别,下面会说明)
1、配置Pom.xml引用spring-cloud-security
和oauth2
的jar包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-security</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
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;
}
}
创建CustomOAuthExceptionSerializer
类
public class CustomOAuthExceptionSerializer extends StdSerializer<CustomOAuthException> {
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<String, String> 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<OAuth2Exception> translate(Exception e) throws Exception {
ResponseEntity<OAuth2Exception> translate = super.translate(e);
OAuth2Exception body = translate.getBody();
CustomOAuthException customOauthException = new CustomOAuthException(body.getMessage(),body.getOAuth2ErrorCode(),body.getHttpErrorCode());
ResponseEntity<OAuth2Exception> 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
}
资源服务的目录结构
认证服务的目录结构
总结
1、springsecurity 自带的异常信息不符合自己的需求的时候就要自定义返回错误信息
2、错误信息返回的错误码如果是401那么照样返回401,如果觉得401不太好可以自行设置
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。