Menangani semi keamanan otentikasi pengecualian dengan @ExceptionHandler
I'm menggunakan Spring MVC's @ControllerAdvice
dan @ExceptionHandler
untuk menangani semua pengecualian dari SISA Api. Itu bekerja dengan baik untuk pengecualian dilemparkan oleh web mvc controller tapi itu tidak bekerja untuk pengecualian dilemparkan oleh musim semi keamanan filter kustom karena mereka jalankan sebelum controller metode yang dipanggil.
Saya punya kebiasaan semi keamanan filter yang tidak berdasarkan token auth:
public class AegisAuthenticationFilter extends GenericFilterBean {
...
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
try {
...
} catch(AuthenticationException authenticationException) {
SecurityContextHolder.clearContext();
authenticationEntryPoint.commence(request, response, authenticationException);
}
}
}
Dengan kebiasaan ini entry point:
@Component("restAuthenticationEntryPoint")
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint{
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authenticationException) throws IOException, ServletException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authenticationException.getMessage());
}
}
Dan dengan ini kelas untuk menangani pengecualian secara global:
@ControllerAdvice
public class RestEntityResponseExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler({ InvalidTokenException.class, AuthenticationException.class })
@ResponseStatus(value = HttpStatus.UNAUTHORIZED)
@ResponseBody
public RestError handleAuthenticationException(Exception ex) {
int errorCode = AegisErrorCode.GenericAuthenticationError;
if(ex instanceof AegisException) {
errorCode = ((AegisException)ex).getCode();
}
RestError re = new RestError(
HttpStatus.UNAUTHORIZED,
errorCode,
"...",
ex.getMessage());
return re;
}
}
Apa yang saya perlu lakukan adalah untuk kembali rinci JSON tubuh bahkan untuk musim semi keamanan AuthenticationException. Apakah ada cara membuat musim semi keamanan AuthenticationEntryPoint dan spring mvc @ExceptionHandler bekerja bersama-sama?
I'm menggunakan semi keamanan 3.1.4 dan spring mvc 3.2.4.
Ok, saya coba seperti yang disarankan menulis json diri dari AuthenticationEntryPoint dan bekerja.
Hanya untuk pengujian aku berubah AutenticationEntryPoint dengan menghapus respon.sendError
Dengan cara ini anda dapat mengirim kustom data json bersama dengan 401 yang tidak sah bahkan jika anda menggunakan Semi Keamanan AuthenticationEntryPoint.
Jelas anda tidak akan membangun json seperti yang saya lakukan untuk tujuan pengujian, tetapi anda akan membuat beberapa kelas contoh.
Ini adalah masalah yang sangat menarik yang musim Semi Keamanan dan Spring Web kerangka ini tidak cukup konsisten dalam cara mereka menangani respon. Saya percaya hal ini untuk mendukung penanganan pesan kesalahan dengan
MessageConverter
dalam cara yang berguna.Saya mencoba untuk menemukan cara yang elegan untuk menyuntikkan
MessageConverter
ke musim Semi Keamanan sehingga mereka bisa menangkap pengecualian dan mengembalikan mereka dalam format yang tepat sesuai dengan isi negosiasi. Masih, saya larutan di bawah ini yang tidak elegan tapi setidaknya membuat penggunaan musim Semi kode.Saya berasumsi bahwa anda tahu cara untuk memasukkan Jackson dan JAXB perpustakaan, jika tidak, tidak ada gunanya untuk melanjutkan. Ada 3 Langkah dalam total.
Langkah 1 - Buat mandiri kelas, menyimpan MessageConverters
Kelas ini tidak memainkan sihir. Itu hanya menyimpan pesan konverter dan prosesor
RequestResponseBodyMethodProcessor
. Sihir adalah dalam bahwa prosesor yang akan melakukan semua pekerjaan yang termasuk konten negosiasi dan mengubah respon tubuh yang sesuai.Langkah 2 - Membuat AuthenticationEntryPoint
Seperti di banyak tutorial, kelas ini sangat penting untuk menerapkan custom penanganan kesalahan.
Langkah 3 - Daftar entry point
Seperti yang disebutkan, saya melakukannya dengan Jawa Config. Saya hanya menunjukkan relevan konfigurasi di sini, harus ada konfigurasi lainnya seperti sesi kewarganegaraan, dll.
Cobalah dengan beberapa otentikasi gagal kasus, ingat header permintaan harus mencakup: Menerima : XXX dan anda harus mendapatkan pengecualian dalam JSON, XML atau format lainnya.
Cara terbaik I've ditemukan adalah untuk mendelegasikan pengecualian untuk HandlerExceptionResolver
kemudian anda dapat menggunakan @ExceptionHandler untuk format respon dengan cara yang anda inginkan.
Dalam kasus musim Semi Boot dan
@EnableResourceServer
, hal ini relatif mudah dan nyaman untuk memperpanjangResourceServerConfigurerAdapter
bukanWebSecurityConfigurerAdapter
di Jawa konfigurasi dan daftar kustomAuthenticationEntryPoint
dengan meng-overridemengkonfigurasi(ResourceServerSecurityConfigurer sumber daya)
dan menggunakansumber daya.authenticationEntryPoint(customAuthEntryPoint())
dalam metode.Sesuatu seperti ini:
Ada's juga bagus
OAuth2AuthenticationEntryPoint
yang dapat diperpanjang (karena itu's tidak final) dan sebagian digunakan kembali sementara menerapkan customAuthenticationEntryPoint
. Secara khusus, ia menambahkan "WWW-Authenticate" header dengan kesalahan-rincian yang terkait.Berharap ini akan membantu seseorang.
Mengambil jawaban dari @Nicola dan @Victor Sayap dan menambahkan lebih banyak cara standar:
Sekarang, anda dapat menyuntikkan dikonfigurasi Jackson, Jaxb atau apapun yang anda gunakan untuk mengkonversi respon tubuh pada MVC penjelasan atau XML berdasarkan konfigurasi dengan serializers, deserializers dan sebagainya.
Kita perlu menggunakan
HandlerExceptionResolver
dalam kasus itu.Juga, anda perlu menambahkan handler pengecualian kelas untuk kembali objek.
semoga anda mendapatkan error pada saat menjalankan proyek karena beberapa implementasi dari
HandlerExceptionResolver
, Dalam hal ini anda harus menambahkan@Qualifier("handlerExceptionResolver")
onHandlerExceptionResolver
Saya mampu untuk mengatasinya hanya dengan meng-override metode 'unsuccessfulAuthentication' di filter saya. Di sana, saya mengirim sebuah kesalahan respon kepada klien dengan yang diinginkan kode status HTTP.
I'm menggunakan objectMapper. Setiap Istirahat Layanan ini sebagian besar bekerja dengan json, dan di salah satu konfigurasi anda telah dikonfigurasi objek mapper.
Kode ini ditulis dalam Kotlin, mudah-mudahan itu akan menjadi ok.
Update: Jika anda suka dan lebih memilih untuk melihat kode secara langsung, maka saya memiliki dua contoh untuk anda, salah satunya dengan menggunakan standar Keamanan musim Semi yang adalah apa yang anda cari, yang lain adalah menggunakan setara dengan Reaktif Web dan Reaktif Keamanan:
Salah satu yang saya selalu gunakan untuk JSON berbasis endpoint terlihat seperti berikut: ``jawa @Komponen public class JwtAuthEntryPoint mengimplementasikan AuthenticationEntryPoint {
@Autowired ObjectMapper mapper;
private static final Logger-logger = LoggerFactory.getLogger(JwtAuthEntryPoint.class);
@Override public void dimulai(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException { // Memanggil ketika pengguna mencoba untuk mengakses sebuah endpoint yang membutuhkan untuk dapat disahkan // kita hanya kembali unauthorizaed logger.kesalahan("kesalahan yang tidak Sah. Pesan - {", e.getMessage());
ServletServerHttpResponse res = new ServletServerHttpResponse(response); res.setStatusCode(HttpStatus.Tidak SAH); res.getServletResponse().setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); res.getBody().menulis(mapper.writeValueAsString(baru ErrorResponse("Anda harus dikonfirmasi")).getBytes()); } } ``
Objek mapper menjadi bean setelah anda menambahkan spring web starter, tapi saya lebih memilih untuk menyesuaikan, jadi di sini adalah saya implementasi untuk ObjectMapper: ``jawa @Bean publik Jackson2ObjectMapperBuilder objectMapperBuilder() { Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder(); pembangun.modul(baru JavaTimeModule());
// contoh: Menggunakan created_at bukan createdAt pembangun.propertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
// skip null bidang pembangun.serializationInclusion(JsonInclude.Sertakan.NON_NULL); pembangun.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); kembali builder; }
Default AuthenticationEntryPoint anda tetapkan dalam WebSecurityConfigurerAdapter kelas:
jawa @Konfigurasi @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfig meluas WebSecurityConfigurerAdapter { // ............ @Autowired pribadi JwtAuthEntryPoint unauthorizedHandler; @Override protected void mengkonfigurasi(HttpSecurity http) throws Exception { http.cors().dan().csrf().menonaktifkan() .authorizeRequests() // .antMatchers("/api/auth", "/api/login", "**").permitAll() .anyRequest().permitAll() .dan() .exceptionHandling().authenticationEntryPoint(unauthorizedHandler) .dan() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);http.header().frameOptions().menonaktifkan(); // jika tidak H2 konsol tidak tersedia // Ada banyak cara untuk cara menempatkan Filter kami dalam posisi dalam rantai // Anda dapat memecahkan setiap kesalahan mengaktifkan debug(lihat di bawah), itu akan mencetak rantai Filter http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class); } // .......... } ``