foreword

I have written before using springsecurity to build an authentication service SpringSecurity+Oauth2: password authorization mode to obtain Token (MongoDB+JWT) . This article writes about how to build a resource service. Only the authenticated token can be accessed.

operate

1. Configure the jar package of Pom.xml that references spring-cloud-security and oauth2
2. Configure the main class @EnableResourceServer annotation to enable resource services
3. Create JWTTokenStoreConfig class, configure and parse token
4. Create ResourceServerConfiguration class configuration access rights and custom exceptions
5. Custom springsecurity exception information ( note: the custom exceptions of authentication and resource services are unified and there is no difference, which will be explained below )

1. Configure the jar package of Pom.xml reference spring-cloud-security and oauth2

 <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. Configure the main class @EnableResourceServer annotation to enable resource services

 @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. Create JWTTokenStoreConfig class, configure and parse 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. Create ResourceServerConfiguration class configuration access rights and custom exceptions

 @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. Custom springsecurity Exception information

Create CustomOAuthException class

 @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;
    }

}

Create CustomOAuthExceptionSerializer class

 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();
    }
}

Create CustomOAuthWebResponseExceptionTranslator class

 @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;
    }
}

Finally, if the username or password is incorrect, the following error message is returned:

 {
  timestamp: 1658149719489,
  path: '/oauth/token',
  error: 'invalid_request',
  message: '用户名或密码错误'
  status: 400
}

If the token is wrong, the following error message is returned:

 {
  timestamp: 1658149719489,
  path: '/oauth/token',
  error: 'invalid_request',
  message: 'token错误或过期'
  status: 400
}

If the refresh_token is wrong, the following error message is returned:

 {
  timestamp: 1658149719489,
  path: '/oauth/token',
  error: 'invalid_request',
  message: 'refresh_token错误或过期'
  status: 400
}

Directory structure of resource services
image.png

Directory Structure of Authentication Service
image.png

Summarize

1. When the exception information that comes with springsecurity does not meet your needs, you must customize the return error information
2. If the error code returned by the error message is 401, then 401 will still be returned. If you think 401 is not very good, you can set it yourself

quote

Custom exceptions for SpringBoot Security


Awbeci
3.1k 声望212 粉丝

Awbeci


引用和评论

1 篇内容引用
0 条评论