{"id":313234,"date":"2020-11-16T21:00:31","date_gmt":"2020-11-16T21:00:31","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=313234"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=313234","title":{"rendered":"Spring Security \u2014 \u043f\u0440\u0438\u043c\u0435\u0440 REST-\u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0441 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0435\u0439 \u043f\u043e \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u0443 OAuth2 \u0447\u0435\u0440\u0435\u0437 BitBucket \u0438 JWT"},"content":{"rendered":"\n<div class=\"post__text post__text-html post__text_v1\" id=\"post-content-body\">\u0412 <a href=\"https:\/\/habr.com\/ru\/post\/497588\/\"> \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0439 <\/a> \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u043b\u0438 \u043f\u0440\u043e\u0441\u0442\u043e\u0435 \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u043e\u0435 \u0432\u0435\u0431 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0434\u043b\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0441\u044f \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b OAuth2 \u0441 Bitbucket \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438. \u041a\u043e\u043c\u0443-\u0442\u043e \u0442\u0430\u043a\u0430\u044f \u0441\u0432\u044f\u0437\u043a\u0430 \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f \u0441\u0442\u0440\u0430\u043d\u043d\u043e\u0439, \u043d\u043e \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u044c\u0442\u0435, \u0447\u0442\u043e \u043c\u044b \u0440\u0430\u0437\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c CI (Continuous Integration) \u0441\u0435\u0440\u0432\u0435\u0440 \u0438 \u0445\u043e\u0442\u0435\u043b\u0438 \u0431\u044b \u0438\u043c\u0435\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0440\u0435\u0441\u0443\u0440\u0441\u0430\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0435 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044f \u0432\u0435\u0440\u0441\u0438\u0439. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043f\u043e \u0442\u0430\u043a\u043e\u043c\u0443 \u0436\u0435 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0443 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0430\u044f CI \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430 <a href=\"https:\/\/drone.io\" rel=\"nofollow\">drone.io<\/a>.<\/p>\n<p>  \u0412 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0430\u0441\u044c HTTP-\u0441\u0435\u0441\u0441\u0438\u044f (\u0438 \u043a\u0443\u043a\u0438). \u041e\u0434\u043d\u0430\u043a\u043e \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 REST-\u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0434\u0430\u043d\u043d\u044b\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u043d\u0435 \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043e\u0434\u043d\u0438\u043c \u0438\u0437 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u0439 REST \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043e\u0442\u0441\u0443\u0442\u0441\u0432\u0438\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f. \u0412 \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c REST-\u0441\u0435\u0440\u0432\u0438\u0441, \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043a \u043a\u043e\u0442\u043e\u0440\u043e\u043c\u0443 \u0431\u0443\u0434\u0435\u0442 \u043e\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043b\u044f\u0442\u044c\u0441\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0442\u043e\u043a\u0435\u043d\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 (access token).<br \/>  <a name=\"habracut\"><\/a>  <\/p>\n<h2>\u041d\u0435\u043c\u043d\u043e\u0433\u043e \u0442\u0435\u043e\u0440\u0438\u0438<\/h2>\n<p>  <b>\u0410\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f<\/b> \u2014 \u044d\u0442\u043e \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0443\u0447\u0451\u0442\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f (\u043b\u043e\u0433\u0438\u043d\/\u043f\u0430\u0440\u043e\u043b\u044c). \u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043f\u043e\u0434\u043b\u0438\u043d\u043d\u043e\u0441\u0442\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043e\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u0443\u0442\u0451\u043c \u0441\u0440\u0430\u0432\u043d\u0435\u043d\u0438\u044f \u0432\u0432\u0435\u0434\u0451\u043d\u043d\u043e\u0433\u043e \u0438\u043c \u043b\u043e\u0433\u0438\u043d\u0430\/\u043f\u0430\u0440\u043e\u043b\u044f \u0441 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u043d\u044b\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u043c\u0438.<br \/>  <b>\u0410\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f<\/b> \u2014 \u044d\u0442\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u043f\u0440\u0430\u0432 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043d\u0430 \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u043c \u0440\u0435\u0441\u0443\u0440\u0441\u0430\u043c. \u0410\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u043f\u0440\u0438 \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043a \u0440\u0435\u0441\u0443\u0440\u0441\u0443.<\/p>\n<p>  \u0420\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043f\u043e\u0440\u044f\u0434\u043e\u043a \u0440\u0430\u0431\u043e\u0442\u044b \u0434\u0432\u0443\u0445 \u0432\u044b\u0448\u0435\u0443\u043f\u043e\u043c\u044f\u043d\u0443\u0442\u044b\u0445 \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u0432 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432.<br \/>  \u0410\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e HTTP-\u0441\u0435\u0441\u0441\u0438\u0438:  <\/p>\n<ul>\n<li>\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e \u043b\u044e\u0431\u044b\u043c \u0438\u0437 \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u0432.<\/li>\n<li>\u041d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435 \u0441\u043e\u0437\u0434\u0430\u0435\u0442\u0441\u044f HTTP-\u0441\u0435\u0441\u0441\u0438\u044f \u0438 \u043a\u0443\u043a\u0438 JSESSIONID, \u0445\u0440\u0430\u043d\u044f\u0449\u0438\u0439 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u0441\u0435\u0441\u0441\u0438\u0438.<\/li>\n<li>\u041a\u0443\u043a\u0438 JSESSIONID \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u0442\u0441\u044f \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442 \u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442\u0441\u044f \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435.<\/li>\n<li>\u0421 \u043a\u0430\u0436\u0434\u044b\u043c \u043f\u043e\u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u043c \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043a\u0443\u043a\u0438 JSESSIONID.<\/li>\n<li>\u0421\u0435\u0440\u0432\u0435\u0440 \u043d\u0430\u0445\u043e\u0434\u0438\u0442 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0443\u044e HTTP-\u0441\u0435\u0441\u0441\u0438\u044e \u0441 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0435\u0439 \u043e \u0442\u0435\u043a\u0443\u0449\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435 \u0438 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u0438\u043c\u0435\u0435\u0442 \u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043f\u0440\u0430\u0432\u0430 \u043d\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u043e\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430.<\/li>\n<li>\u0414\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0432\u044b\u0445\u043e\u0434\u0430 \u0438\u0437 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 HTTP-\u0441\u0435\u0441\u0441\u0438\u044e.<\/li>\n<\/ul>\n<p>  \u0410\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0442\u043e\u043a\u0435\u043d\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u0430:  <\/p>\n<ul>\n<li>\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e \u043b\u044e\u0431\u044b\u043c \u0438\u0437 \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u0432.<\/li>\n<li>\u0421\u0435\u0440\u0432\u0435\u0440 \u0441\u043e\u0437\u0434\u0430\u0435\u0442 \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430, \u043f\u043e\u0434\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0439 \u0441\u0435\u043a\u0440\u0435\u0442\u043d\u044b\u043c \u043a\u043b\u044e\u0447\u043e\u043c, \u0430 \u0437\u0430\u0442\u0435\u043c \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0435\u0433\u043e \u043a\u043b\u0438\u0435\u043d\u0442\u0443. \u0422\u043e\u043a\u0435\u043d \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u0435\u0433\u043e \u0440\u043e\u043b\u0438.<\/li>\n<li>\u0422\u043e\u043a\u0435\u043d \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442\u0441\u044f \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435 \u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u0442\u0441\u044f \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 \u0441 \u043a\u0430\u0436\u0434\u044b\u043c \u043f\u043e\u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u043c. \u041a\u0430\u043a \u043f\u0440\u0430\u0432\u0438\u043b\u043e \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u0442\u043e\u043a\u0435\u043d\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u044f HTTP \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a Authorization.<\/li>\n<li>\u0421\u0435\u0440\u0432\u0435\u0440 \u0441\u0432\u0435\u0440\u044f\u0435\u0442 \u043f\u043e\u0434\u043f\u0438\u0441\u044c \u0442\u043e\u043a\u0435\u043d\u0430, \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0435\u0442 \u0438\u0437 \u043d\u0435\u0433\u043e \u0438\u0434\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0442\u043e\u0440 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u0435\u0433\u043e \u0440\u043e\u043b\u0438 \u0438 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u0438\u043c\u0435\u0435\u0442 \u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043f\u0440\u0430\u0432\u0430 \u043d\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u043e\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430.<\/li>\n<li>\u0414\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0432\u044b\u0445\u043e\u0434\u0430 \u0438\u0437 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u0442\u043e\u043a\u0435\u043d \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435 \u0431\u0435\u0437 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c.<\/li>\n<\/ul>\n<p>  \u0420\u0430\u0441\u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0435\u043d\u043d\u044b\u043c \u0444\u043e\u0440\u043c\u0430\u0442\u043e\u043c \u0442\u043e\u043a\u0435\u043d\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0432 \u043d\u0430\u0441\u0442\u043e\u044f\u0449\u0435\u0435 \u0432\u0440\u0435\u043c\u044f \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f JSON Web Token (JWT). \u0422\u043e\u043a\u0435\u043d \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 JWT \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u0442\u0440\u0438 \u0431\u043b\u043e\u043a\u0430, \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0445 \u0442\u043e\u0447\u043a\u0430\u043c\u0438: \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a (header), \u043d\u0430\u0431\u043e\u0440 \u043f\u043e\u043b\u0435\u0439 (payload) \u0438 \u0441\u0438\u0433\u043d\u0430\u0442\u0443\u0440\u0443 (\u043f\u043e\u0434\u043f\u0438\u0441\u044c). \u041f\u0435\u0440\u0432\u044b\u0435 \u0434\u0432\u0430 \u0431\u043b\u043e\u043a\u0430 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u044b \u0432 JSON-\u0444\u043e\u0440\u043c\u0430\u0442\u0435 \u0438 \u0437\u0430\u043a\u043e\u0434\u0438\u0440\u043e\u0432\u0430\u043d\u044b \u0432 \u0444\u043e\u0440\u043c\u0430\u0442 base64. \u041d\u0430\u0431\u043e\u0440 \u043f\u043e\u043b\u0435\u0439 \u043c\u043e\u0436\u0435\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u0442\u044c \u043a\u0430\u043a \u0438\u0437 \u0437\u0430\u0440\u0435\u0437\u0435\u0440\u0432\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u0438\u043c\u0435\u043d (iss, iat, exp), \u0442\u0430\u043a \u0438 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u043b\u044c\u043d\u044b\u0445 \u043f\u0430\u0440 \u0438\u043c\u044f\/\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435. \u041f\u043e\u0434\u043f\u0438\u0441\u044c \u043c\u043e\u0436\u0435\u0442 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043a\u0430\u043a \u043f\u0440\u0438 \u043f\u043e\u043c\u043e\u0449\u0438 \u0441\u0438\u043c\u043c\u0435\u0442\u0440\u0438\u0447\u043d\u044b\u0445, \u0442\u0430\u043a \u0438 \u0430\u0441\u0438\u043c\u043c\u0435\u0442\u0440\u0438\u0447\u043d\u044b\u0445 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u043e\u0432 \u0448\u0438\u0444\u0440\u043e\u0432\u0430\u043d\u0438\u044f.<\/p>\n<h2>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f<\/h2>\n<p>  \u041c\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c REST-\u0441\u0435\u0440\u0432\u0438\u0441, \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0449\u0438\u0439 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435 API:  <\/p>\n<ul>\n<li>GET \/auth\/login \u2014 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f.<\/li>\n<li>POST \/auth\/token \u2014 \u0437\u0430\u043f\u0440\u043e\u0441\u0438\u0442\u044c \u043d\u043e\u0432\u0443\u044e \u043f\u0430\u0440\u0443 access\/refresh \u0442\u043e\u043a\u0435\u043d\u043e\u0432.<\/li>\n<li>GET \/api\/repositories \u2014 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a Bitbucket \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0435\u0432 \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f.<\/li>\n<\/ul>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/ka\/tz\/f6\/katzf69ynru_1qqezdgydfibxey.jpeg\"><br \/>  \u0412\u044b\u0441\u043e\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u0430\u044f \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.<\/p>\n<p>  \u0417\u0430\u043c\u0435\u0442\u0438\u043c, \u0447\u0442\u043e \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0438\u0437 \u0442\u0440\u0435\u0445 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432, \u043f\u043e\u043c\u0438\u043c\u043e \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u043c\u044b \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443, Bitbucket \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0443\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u043a \u043d\u0435\u043c\u0443. \u041c\u044b \u043d\u0435 \u0431\u0443\u0434\u0435\u043c \u043d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0442\u044c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044e \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u043f\u043e \u0440\u043e\u043b\u044f\u043c, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0434\u0435\u043b\u0430\u0442\u044c \u043f\u0440\u0438\u043c\u0435\u0440 \u0441\u043b\u043e\u0436\u043d\u0435\u0435. \u0423 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u0438\u043d API \u043c\u0435\u0442\u043e\u0434 GET \/api\/repositories, \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u043e\u0433\u0443\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438. \u0421\u0435\u0440\u0432\u0435\u0440 \u043c\u043e\u0436\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u043d\u0430 Bitbucket \u043b\u044e\u0431\u044b\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438, \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u043d\u044b\u0435 \u043f\u0440\u0438 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 OAuth \u043a\u043b\u0438\u0435\u043d\u0442\u0430.<br \/>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/kg\/up\/wr\/kgupwrzpy6zo8e-zcipc7lxrnzc.png\"><br \/>  \u041f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 OAuth \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u043e\u043f\u0438\u0441\u0430\u043d \u0432 <a href=\"https:\/\/habr.com\/ru\/post\/497588\/\"> \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0439<\/a> \u0441\u0442\u0430\u0442\u044c\u0435.<\/p>\n<p>  \u0414\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c Spring Boot \u0432\u0435\u0440\u0441\u0438\u0438 2.2.2.RELEASE \u0438 Spring Security \u0432\u0435\u0440\u0441\u0438\u0438 5.2.1.RELEASE.<\/p>\n<h3>\u041f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c AuthenticationEntryPoint.<\/h3>\n<p>  \u0412 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u043c \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438, \u043a\u043e\u0433\u0434\u0430 \u043e\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u0435 \u043a \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u043e\u043c\u0443 \u0440\u0435\u0441\u0443\u0440\u0441\u0443 \u0438 \u0432 \u0441\u0435\u043a\u044c\u044e\u0440\u0438\u0442\u0438 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0435 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u043e\u0431\u044a\u0435\u043a\u0442 Authentication, Spring Security \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438. \u041e\u0434\u043d\u0430\u043a\u043e \u0434\u043b\u044f REST-\u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0431\u043e\u043b\u0435\u0435 \u043f\u043e\u0434\u0445\u043e\u0434\u044f\u0449\u0438\u043c \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435\u043c \u0432 \u044d\u0442\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0431\u044b\u043b\u043e \u0431\u044b \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c HTTP \u0441\u0442\u0430\u0442\u0443\u0441 401 (UNAUTHORIZED).<\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">RestAuthenticationEntryPoint<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"java\">public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {      @Override     public void commence(             HttpServletRequest request,             HttpServletResponse response,             AuthenticationException authException) throws IOException {         response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException.getMessage());     } } <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  <\/p>\n<h3>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c login endpoint.<\/h3>\n<p>  \u0414\u043b\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043c\u044b \u043f\u043e-\u043f\u0440\u0435\u0436\u043d\u0435\u043c\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c OAuth2 \u0441 \u0442\u0438\u043f\u043e\u043c \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 Authorization Code. \u041e\u0434\u043d\u0430\u043a\u043e \u043d\u0430 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u043c \u0448\u0430\u0433\u0435 \u043c\u044b \u0437\u0430\u043c\u0435\u043d\u0438\u043b\u0438 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 AuthenticationEntryPoint \u0441\u0432\u043e\u0435\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0435\u0439, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0430\u043c \u043d\u0443\u0436\u0435\u043d \u044f\u0432\u043d\u044b\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438. \u041f\u0440\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0435 GET \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443 \/auth\/login \u043c\u044b \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u0438\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 Bitbucket. \u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u043c \u044d\u0442\u043e\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u0430 \u0431\u0443\u0434\u0435\u0442 URL \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430, \u043f\u043e \u043a\u043e\u0442\u043e\u0440\u043e\u043c\u0443 \u043c\u044b \u0432\u043e\u0437\u0432\u0440\u0430\u0442\u0438\u043c \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043f\u043e\u0441\u043b\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438.<\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">Login endpoint<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"java\">@Path(&quot;\/auth&quot;) public class AuthEndpoint extends EndpointBase {  ...      @GET     @Path(&quot;\/login&quot;)     public Response authorize(@QueryParam(REDIRECT_URI) String redirectUri) {         String authUri = &quot;\/oauth2\/authorization\/bitbucket&quot;;         UriComponentsBuilder builder = fromPath(authUri).queryParam(REDIRECT_URI, redirectUri);         return handle(() -&gt; temporaryRedirect(builder.build().toUri()).build());     } } <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  <\/p>\n<h3>\u041f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c AuthenticationSuccessHandler.<\/h3>\n<p>  AuthenticationSuccessHandler \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043f\u043e\u0441\u043b\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438. \u0421\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c \u0442\u0443\u0442 \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430, refresh \u0442\u043e\u043a\u0435\u043d \u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u043c \u0440\u0435\u0434\u0438\u0440\u0435\u043a\u0442 \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443 \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u044b\u043b \u043f\u0435\u0440\u0435\u0434\u0430\u043d \u0432 \u043d\u0430\u0447\u0430\u043b\u0435 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438. \u0422\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0432\u0435\u0440\u043d\u0435\u043c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u043c GET \u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u0430 refresh \u0442\u043e\u043a\u0435\u043d \u0432 httpOnly \u043a\u0443\u043a\u0435. \u0427\u0442\u043e \u0442\u0430\u043a\u043e\u0435 refresh \u0442\u043e\u043a\u0435\u043d \u0440\u0430\u0437\u0431\u0435\u0440\u0435\u043c \u043f\u043e\u0437\u0436\u0435.<\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">ExampleAuthenticationSuccessHandler<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"java\">public class ExampleAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {      private final TokenService tokenService;      private final AuthProperties authProperties;      private final HttpCookieOAuth2AuthorizationRequestRepository authorizationRequestRepository;      public ExampleAuthenticationSuccessHandler(             TokenService tokenService,             AuthProperties authProperties,             HttpCookieOAuth2AuthorizationRequestRepository authorizationRequestRepository) {         this.tokenService = requireNonNull(tokenService);         this.authProperties = requireNonNull(authProperties);         this.authorizationRequestRepository = requireNonNull(authorizationRequestRepository);     }      @Override     public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {         log.info(&quot;Logged in user {}&quot;, authentication.getPrincipal());         super.onAuthenticationSuccess(request, response, authentication);     }      @Override     protected String determineTargetUrl(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {         Optional&lt;String&gt; redirectUri = getCookie(request, REDIRECT_URI).map(Cookie::getValue);          if (redirectUri.isPresent() &amp;&amp; !isAuthorizedRedirectUri(redirectUri.get())) {             throw new BadRequestException(&quot;Received unauthorized redirect URI.&quot;);         }          return UriComponentsBuilder.fromUriString(redirectUri.orElse(getDefaultTargetUrl()))                 .queryParam(&quot;token&quot;, tokenService.newAccessToken(toUserContext(authentication)))                 .build().toUriString();     }      @Override     protected void handle(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {         redirectToTargetUrl(request, response, authentication);     }      private boolean isAuthorizedRedirectUri(String uri) {         URI clientRedirectUri = URI.create(uri);         return authProperties.getAuthorizedRedirectUris()                 .stream()                 .anyMatch(authorizedRedirectUri -&gt; {                     \/\/ Only validate host and port. Let the clients use different paths if they want to.                     URI authorizedURI = URI.create(authorizedRedirectUri);                     return authorizedURI.getHost().equalsIgnoreCase(clientRedirectUri.getHost())                             &amp;&amp; authorizedURI.getPort() == clientRedirectUri.getPort();                 });     }      private TokenService.UserContext toUserContext(Authentication authentication) {         ExampleOAuth2User principal = (ExampleOAuth2User) authentication.getPrincipal();         return TokenService.UserContext.builder()                 .login(principal.getName())                 .name(principal.getFullName())                 .build();     }      private void addRefreshTokenCookie(HttpServletResponse response, Authentication authentication) {         RefreshToken token = tokenService.newRefreshToken(toUserContext(authentication));         addCookie(response, REFRESH_TOKEN, token.getId(), (int) token.getValiditySeconds());     }      private void redirectToTargetUrl(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {         String targetUrl = determineTargetUrl(request, response, authentication);          if (response.isCommitted()) {             logger.debug(&quot;Response has already been committed. Unable to redirect to &quot; + targetUrl);             return;         }          addRefreshTokenCookie(response, authentication);         authorizationRequestRepository.removeAuthorizationRequestCookies(request, response);         getRedirectStrategy().sendRedirect(request, response, targetUrl);     } } <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  <\/p>\n<h3>\u041f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c AuthenticationFailureHandler.<\/h3>\n<p>  \u0412 \u0441\u043b\u0443\u0447\u0430\u0435, \u0435\u0441\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043d\u0435 \u043f\u0440\u043e\u0448\u0435\u043b \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e, \u043c\u044b \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u0438\u043c \u0435\u0433\u043e \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443 \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u044b\u043b \u043f\u0435\u0440\u0435\u0434\u0430\u043d \u0432 \u043d\u0430\u0447\u0430\u043b\u0435 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0441 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u043c error, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u043c \u0442\u0435\u043a\u0441\u0442 \u043e\u0448\u0438\u0431\u043a\u0438.<\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">ExampleAuthenticationFailureHandler<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"java\">public class ExampleAuthenticationFailureHandler implements AuthenticationFailureHandler {      private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();      private final HttpCookieOAuth2AuthorizationRequestRepository authorizationRequestRepository;      public ExampleAuthenticationFailureHandler(             HttpCookieOAuth2AuthorizationRequestRepository authorizationRequestRepository) {         this.authorizationRequestRepository = requireNonNull(authorizationRequestRepository);     }      @Override     public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException {         String targetUrl = getFailureUrl(request, exception);         authorizationRequestRepository.removeAuthorizationRequestCookies(request, response);         redirectStrategy.sendRedirect(request, response, targetUrl);     }      private String getFailureUrl(HttpServletRequest request, AuthenticationException exception) {         String targetUrl = getCookie(request, Cookies.REDIRECT_URI)                 .map(Cookie::getValue)                 .orElse((&quot;\/&quot;));          return UriComponentsBuilder.fromUriString(targetUrl)                 .queryParam(&quot;error&quot;, exception.getLocalizedMessage())                 .build().toUriString();     } } <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  <\/p>\n<h3>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c TokenAuthenticationFilter.<\/h3>\n<p>  \u0417\u0430\u0434\u0430\u0447\u0430 \u044d\u0442\u043e\u0433\u043e \u0444\u0438\u043b\u044c\u0442\u0440\u0430 \u0438\u0437\u0432\u043b\u0435\u0447\u044c \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0438\u0437 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0430 Authorization \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u0435\u0433\u043e \u043d\u0430\u043b\u0438\u0447\u0438\u044f, \u043f\u0440\u043e\u0432\u0430\u043b\u0438\u0434\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u0438 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0435\u043a\u044c\u044e\u0440\u0438\u0442\u0438 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442.<\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">TokenAuthenticationFilter<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"java\">public class TokenAuthenticationFilter extends OncePerRequestFilter {      private final UserService userService;      private final TokenService tokenService;      public TokenAuthenticationFilter(             UserService userService, TokenService tokenService) {         this.userService = requireNonNull(userService);         this.tokenService = requireNonNull(tokenService);     }      @Override     protected void doFilterInternal(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull FilterChain chain) throws ServletException, IOException {         try {             Optional&lt;String&gt; jwtOpt = getJwtFromRequest(request);             if (jwtOpt.isPresent()) {                 String jwt = jwtOpt.get();                 if (isNotEmpty(jwt) &amp;&amp; tokenService.isValidAccessToken(jwt)) {                     String login = tokenService.getUsername(jwt);                     Optional&lt;User&gt; userOpt = userService.findByLogin(login);                     if (userOpt.isPresent()) {                         User user = userOpt.get();                         ExampleOAuth2User oAuth2User = new ExampleOAuth2User(user);                         OAuth2AuthenticationToken authentication = new OAuth2AuthenticationToken(oAuth2User, oAuth2User.getAuthorities(), oAuth2User.getProvider());                         authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));                          SecurityContextHolder.getContext().setAuthentication(authentication);                     }                 }             }         } catch (Exception e) {             logger.error(&quot;Could not set user authentication in security context&quot;, e);         }          chain.doFilter(request, response);     }      private Optional&lt;String&gt; getJwtFromRequest(HttpServletRequest request) {         String token = request.getHeader(AUTHORIZATION);         if (isNotEmpty(token) &amp;&amp; token.startsWith(&quot;Bearer &quot;)) {             token = token.substring(7);         }         return Optional.ofNullable(token);     } } <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  <\/p>\n<h3>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c refresh token endpoint.<\/h3>\n<p>  \u0412 \u0446\u0435\u043b\u044f\u0445 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u043e\u0441\u0442\u0438 \u0432\u0440\u0435\u043c\u044f \u0436\u0438\u0437\u043d\u0438 \u0442\u043e\u043a\u0435\u043d\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043e\u0431\u044b\u0447\u043d\u043e \u0434\u0435\u043b\u0430\u044e\u0442 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0438\u043c. \u0422\u043e\u0433\u0434\u0430 \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u0435\u0433\u043e \u043a\u0440\u0430\u0436\u0438 \u0437\u043b\u043e\u0443\u043c\u044b\u0448\u043b\u0435\u043d\u043d\u0438\u043a \u043d\u0435 \u0441\u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0438\u043c \u0431\u0435\u0441\u043a\u043e\u043d\u0435\u0447\u043d\u043e \u0434\u043e\u043b\u0433\u043e. \u0427\u0442\u043e\u0431\u044b \u043d\u0435 \u0437\u0430\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u0432\u0445\u043e\u0434 \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441\u043d\u043e\u0432\u0430 \u0438 \u0441\u043d\u043e\u0432\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f refresh \u0442\u043e\u043a\u0435\u043d. \u041e\u043d \u0432\u044b\u0434\u0430\u0435\u0442\u0441\u044f \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c \u043f\u043e\u0441\u043b\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u0439 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 \u0442\u043e\u043a\u0435\u043d\u043e\u043c \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0438 \u0438\u043c\u0435\u0435\u0442 \u0431\u043e\u043b\u044c\u0448\u0435\u0435 \u0432\u0440\u0435\u043c\u044f \u0436\u0438\u0437\u043d\u0438. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0435\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u0438\u0442\u044c \u043d\u043e\u0432\u0443\u044e \u043f\u0430\u0440\u0443 \u0442\u043e\u043a\u0435\u043d\u043e\u0432. Refresh \u0442\u043e\u043a\u0435\u043d \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u044e\u0442 \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0432 httpOnly \u043a\u0443\u043a\u0435.<\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">Refresh token endpoint<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"java\">@Path(&quot;\/auth&quot;) public class AuthEndpoint extends EndpointBase {  ...      @POST     @Path(&quot;\/token&quot;)     @Produces(APPLICATION_JSON)     public Response refreshToken(@CookieParam(REFRESH_TOKEN) String refreshToken) {         return handle(() -&gt; {             if (refreshToken == null) {                 throw new InvalidTokenException(&quot;Refresh token was not provided.&quot;);             }             RefreshToken oldRefreshToken = tokenService.findRefreshToken(refreshToken);             if (oldRefreshToken == null || !tokenService.isValidRefreshToken(oldRefreshToken)) {                 throw new InvalidTokenException(&quot;Refresh token is not valid or expired.&quot;);             }              Map&lt;String, String&gt; result = new HashMap&lt;&gt;();             result.put(&quot;token&quot;, tokenService.newAccessToken(of(oldRefreshToken.getUser())));              RefreshToken newRefreshToken = newRefreshTokenFor(oldRefreshToken.getUser());             return Response.ok(result).cookie(createRefreshTokenCookie(newRefreshToken)).build();         });     } } <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  <\/p>\n<h3>\u041f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c AuthorizationRequestRepository.<\/h3>\n<p>  Spring Security \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u043e\u0431\u044a\u0435\u043a\u0442 AuthorizationRequestRepository \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 OAuth2AuthorizationRequest \u043d\u0430 \u0432\u0440\u0435\u043c\u044f \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438. \u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0435\u0439 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043a\u043b\u0430\u0441\u0441 HttpSessionOAuth2AuthorizationRequestRepository, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 HTTP-\u0441\u0435\u0441\u0441\u0438\u044e \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0430. \u0422.\u043a. \u043d\u0430\u0448 \u0441\u0435\u0440\u0432\u0438\u0441 \u043d\u0435 \u0434\u043e\u043b\u0436\u0435\u043d \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435, \u044d\u0442\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043d\u0430\u043c \u043d\u0435 \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442. \u0420\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u0441\u0432\u043e\u0439 \u043a\u043b\u0430\u0441\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c HTTP cookies.<\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">HttpCookieOAuth2AuthorizationRequestRepository<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"java\">public class HttpCookieOAuth2AuthorizationRequestRepository implements AuthorizationRequestRepository&lt;OAuth2AuthorizationRequest&gt; {      private static final int COOKIE_EXPIRE_SECONDS = 180;      private static final String OAUTH2_AUTHORIZATION_REQUEST_COOKIE_NAME = &quot;OAUTH2-AUTH-REQUEST&quot;;      @Override     public OAuth2AuthorizationRequest loadAuthorizationRequest(HttpServletRequest request) {         return getCookie(request, OAUTH2_AUTHORIZATION_REQUEST_COOKIE_NAME)                 .map(cookie -&gt; deserialize(cookie, OAuth2AuthorizationRequest.class))                 .orElse(null);     }      @Override     public void saveAuthorizationRequest(OAuth2AuthorizationRequest authorizationRequest, HttpServletRequest request, HttpServletResponse response) {         if (authorizationRequest == null) {             removeAuthorizationRequestCookies(request, response);             return;         }          addCookie(response, OAUTH2_AUTHORIZATION_REQUEST_COOKIE_NAME, serialize(authorizationRequest), COOKIE_EXPIRE_SECONDS);         String redirectUriAfterLogin = request.getParameter(QueryParams.REDIRECT_URI);         if (isNotBlank(redirectUriAfterLogin)) {             addCookie(response, REDIRECT_URI, redirectUriAfterLogin, COOKIE_EXPIRE_SECONDS);         }     }      @Override     public OAuth2AuthorizationRequest removeAuthorizationRequest(HttpServletRequest request) {         return loadAuthorizationRequest(request);     }      public void removeAuthorizationRequestCookies(HttpServletRequest request, HttpServletResponse response) {         deleteCookie(request, response, OAUTH2_AUTHORIZATION_REQUEST_COOKIE_NAME);         deleteCookie(request, response, REDIRECT_URI);     }      private static String serialize(Object object) {         return Base64.getUrlEncoder().encodeToString(SerializationUtils.serialize(object));     }      @SuppressWarnings(&quot;SameParameterValue&quot;)     private static &lt;T&gt; T deserialize(Cookie cookie, Class&lt;T&gt; clazz) {         return clazz.cast(SerializationUtils.deserialize(Base64.getUrlDecoder().decode(cookie.getValue())));     } } <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  <\/p>\n<h3>\u041d\u0430\u0441\u0442\u0440\u043e\u0438\u043c Spring Security.<\/h3>\n<p>  \u0421\u043e\u0431\u0435\u0440\u0435\u043c \u0432\u0441\u0435 \u043f\u0440\u043e\u0434\u0435\u043b\u0430\u043d\u043d\u043e\u0435 \u0432\u044b\u0448\u0435 \u0432\u043c\u0435\u0441\u0442\u0435 \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u043c Spring Security.<\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">WebSecurityConfig<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"java\">@Configuration @EnableWebSecurity public static class WebSecurityConfig extends WebSecurityConfigurerAdapter {      private final ExampleOAuth2UserService userService;      private final TokenAuthenticationFilter tokenAuthenticationFilter;      private final AuthenticationFailureHandler authenticationFailureHandler;      private final AuthenticationSuccessHandler authenticationSuccessHandler;      private final HttpCookieOAuth2AuthorizationRequestRepository authorizationRequestRepository;      @Autowired     public WebSecurityConfig(             ExampleOAuth2UserService userService,             TokenAuthenticationFilter tokenAuthenticationFilter,             AuthenticationFailureHandler authenticationFailureHandler,             AuthenticationSuccessHandler authenticationSuccessHandler,             HttpCookieOAuth2AuthorizationRequestRepository authorizationRequestRepository) {         this.userService = userService;         this.tokenAuthenticationFilter = tokenAuthenticationFilter;         this.authenticationFailureHandler = authenticationFailureHandler;         this.authenticationSuccessHandler = authenticationSuccessHandler;         this.authorizationRequestRepository = authorizationRequestRepository;     }      @Override     protected void configure(HttpSecurity http) throws Exception {         http                 .cors().and()                 .csrf().disable()                 .formLogin().disable()                 .httpBasic().disable()                 .sessionManagement(sm -&gt; sm.sessionCreationPolicy(STATELESS))                 .exceptionHandling(eh -&gt; eh                         .authenticationEntryPoint(new RestAuthenticationEntryPoint())                 )                 .authorizeRequests(authorizeRequests -&gt; authorizeRequests                         .antMatchers(&quot;\/auth\/**&quot;).permitAll()                         .anyRequest().authenticated()                 )                 .oauth2Login(oauth2Login -&gt; oauth2Login                         .failureHandler(authenticationFailureHandler)                         .successHandler(authenticationSuccessHandler)                         .userInfoEndpoint(userInfoEndpoint -&gt; userInfoEndpoint.userService(userService))                         .authorizationEndpoint(authEndpoint -&gt; authEndpoint.authorizationRequestRepository(authorizationRequestRepository))                 );          http.addFilterBefore(tokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);     } } <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  <\/p>\n<h3>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c repositories endpoint.<\/h3>\n<p>  \u0422\u043e \u0440\u0430\u0434\u0438 \u0447\u0435\u0433\u043e \u0438 \u043d\u0443\u0436\u043d\u0430 \u0431\u044b\u043b\u0430 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0447\u0435\u0440\u0435\u0437 OAuth2 \u0438 Bitbucket \u2014 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c Bitbucket API \u0434\u043b\u044f \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0441\u0432\u043e\u0438\u043c \u0440\u0435\u0441\u0443\u0440\u0441\u0430\u043c. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c Bitbucket repositories API \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0441\u043f\u0438\u0441\u043a\u0430 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0435\u0432 \u0442\u0435\u043a\u0443\u0449\u0435\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f.<\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">Repositories endpoint<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"java\">@Path(&quot;\/api&quot;) public class ApiEndpoint extends EndpointBase {      @Autowired     private BitbucketService bitbucketService;      @GET     @Path(&quot;\/repositories&quot;)     @Produces(APPLICATION_JSON)     public List&lt;Repository&gt; getRepositories() {         return handle(bitbucketService::getRepositories);     } }  public class BitbucketServiceImpl implements BitbucketService {      private static final String BASE_URL = &quot;https:\/\/api.bitbucket.org&quot;;      private final Supplier&lt;RestTemplate&gt; restTemplate;      public BitbucketServiceImpl(Supplier&lt;RestTemplate&gt; restTemplate) {         this.restTemplate = restTemplate;     }      @Override     public List&lt;Repository&gt; getRepositories() {         UriComponentsBuilder uriBuilder = fromHttpUrl(format(&quot;%s\/2.0\/repositories&quot;, BASE_URL));         uriBuilder.queryParam(&quot;role&quot;, &quot;member&quot;);          ResponseEntity&lt;BitbucketRepositoriesResponse&gt; response = restTemplate.get().exchange(                 uriBuilder.toUriString(),                 HttpMethod.GET,                 new HttpEntity&lt;&gt;(new HttpHeadersBuilder()                         .acceptJson()                         .build()),                 BitbucketRepositoriesResponse.class);          BitbucketRepositoriesResponse body = response.getBody();         return body == null ? emptyList() : extractRepositories(body);     }      private List&lt;Repository&gt; extractRepositories(BitbucketRepositoriesResponse response) {         return response.getValues() == null                 ? emptyList()                 : response.getValues().stream().map(BitbucketServiceImpl.this::convertRepository).collect(toList());     }      private Repository convertRepository(BitbucketRepository bbRepo) {         Repository repo = new Repository();         repo.setId(bbRepo.getUuid());         repo.setFullName(bbRepo.getFullName());         return repo;     } } <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  <\/p>\n<h2>\u0422\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435<\/h2>\n<p>  \u0414\u043b\u044f \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439 HTTP-\u0441\u0435\u0440\u0432\u0435\u0440, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u0435\u043d \u0442\u043e\u043a\u0435\u043d \u0434\u043e\u0441\u0442\u0443\u043f\u0430. \u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u0432\u044b\u0437\u0432\u0430\u0442\u044c repositories endpoint \u0431\u0435\u0437 \u0442\u043e\u043a\u0435\u043d\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0438 \u0443\u0431\u0435\u0434\u0438\u043c\u0441\u044f, \u0447\u0442\u043e \u0432 \u044d\u0442\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u043e\u0448\u0438\u0431\u043a\u0443 401. \u0417\u0430\u0442\u0435\u043c \u043f\u0440\u043e\u0439\u0434\u0435\u043c \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u043c \u0441\u0435\u0440\u0432\u0435\u0440 \u0438 \u043f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435 \u043f\u043e \u0430\u0434\u0440\u0435\u0441\u0443 <a href=\"http:\/\/localhost:8080\/auth\/login\" rel=\"nofollow\">http:\/\/localhost:8080\/auth\/login<\/a>. \u041f\u043e\u0441\u043b\u0435 \u0442\u043e\u0433\u043e \u043a\u0430\u043a \u043c\u044b \u0432\u0432\u0435\u0434\u0435\u043c \u043b\u043e\u0433\u0438\u043d\/\u043f\u0430\u0440\u043e\u043b\u044c, \u043a\u043b\u0438\u0435\u043d\u0442 \u043f\u043e\u043b\u0443\u0447\u0438\u0442 \u0442\u043e\u043a\u0435\u043d \u0438 \u0432\u044b\u0437\u043e\u0432\u0435\u0442 repositories endpoint \u0435\u0449\u0435 \u0440\u0430\u0437. \u0417\u0430\u0442\u0435\u043c \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u043f\u0440\u043e\u0448\u0435\u043d \u043d\u043e\u0432\u044b\u0439 \u0442\u043e\u043a\u0435\u043d \u0438 \u0441\u043d\u043e\u0432\u0430 \u0432\u044b\u0437\u0432\u0430\u043d repositories endpoint \u0441 \u043d\u043e\u0432\u044b\u043c \u0442\u043e\u043a\u0435\u043d\u043e\u043c.<\/p>\n<div class=\"spoiler\" role=\"button\" tabindex=\"0\">                         <b class=\"spoiler_title\">OAuth2JwtExampleClient<\/b>                         <\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"java\">public class OAuth2JwtExampleClient {      \/**      * Start client, then navigate to http:\/\/localhost:8080\/auth\/login.      *\/     public static void main(String[] args) throws Exception {         AuthCallbackHandler authEndpoint = new AuthCallbackHandler(8081);         authEndpoint.start(SOCKET_READ_TIMEOUT, true);          HttpResponse response = getRepositories(null);         assert (response.getStatusLine().getStatusCode() == SC_UNAUTHORIZED);          Tokens tokens = authEndpoint.getTokens();         System.out.println(&quot;Received tokens: &quot; + tokens);         response = getRepositories(tokens.getAccessToken());         assert (response.getStatusLine().getStatusCode() == SC_OK);         System.out.println(&quot;Repositories: &quot; + IOUtils.toString(response.getEntity().getContent(), UTF_8));          \/\/ emulate token usage - wait for some time until iat and exp attributes get updated         \/\/ otherwise we will receive the same token         Thread.sleep(5000);          tokens = refreshToken(tokens.getRefreshToken());         System.out.println(&quot;Refreshed tokens: &quot; + tokens);          \/\/ use refreshed token         response = getRepositories(tokens.getAccessToken());         assert (response.getStatusLine().getStatusCode() == SC_OK);     }      private static Tokens refreshToken(String refreshToken) throws IOException {         BasicClientCookie cookie = new BasicClientCookie(REFRESH_TOKEN, refreshToken);         cookie.setPath(&quot;\/&quot;);         cookie.setDomain(&quot;localhost&quot;);         BasicCookieStore cookieStore = new BasicCookieStore();         cookieStore.addCookie(cookie);          HttpPost request = new HttpPost(&quot;http:\/\/localhost:8080\/auth\/token&quot;);         request.setHeader(ACCEPT, APPLICATION_JSON.getMimeType());          HttpClient httpClient = HttpClientBuilder.create().setDefaultCookieStore(cookieStore).build();         HttpResponse execute = httpClient.execute(request);          Gson gson = new Gson();         Type type = new TypeToken&lt;Map&lt;String, String&gt;&gt;() {         }.getType();         Map&lt;String, String&gt; response = gson.fromJson(IOUtils.toString(execute.getEntity().getContent(), UTF_8), type);          Cookie refreshTokenCookie = cookieStore.getCookies().stream()                 .filter(c -&gt; REFRESH_TOKEN.equals(c.getName()))                 .findAny()                 .orElseThrow(() -&gt; new IOException(&quot;Refresh token cookie not found.&quot;));         return Tokens.of(response.get(&quot;token&quot;), refreshTokenCookie.getValue());     }      private static HttpResponse getRepositories(String accessToken) throws IOException {         HttpClient httpClient = HttpClientBuilder.create().build();         HttpGet request = new HttpGet(&quot;http:\/\/localhost:8080\/api\/repositories&quot;);         request.setHeader(ACCEPT, APPLICATION_JSON.getMimeType());         if (accessToken != null) {             request.setHeader(AUTHORIZATION, &quot;Bearer &quot; + accessToken);         }         return httpClient.execute(request);     } } <\/code><\/pre>\n<p>  <\/div>\n<\/p><\/div>\n<p>  \u041a\u043e\u043d\u0441\u043e\u043b\u044c\u043d\u044b\u0439 \u0432\u044b\u0432\u043e\u0434 \u043a\u043b\u0438\u0435\u043d\u0442\u0430.  <\/p>\n<pre><code class=\"plaintext\">Received tokens: Tokens(accessToken=eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJldm9sdmVjaS10ZXN0a2l0IiwidXNlcm5hbWUiOiJFdm9sdmVDSSBUZXN0a2l0IiwiaWF0IjoxNjA1NDY2MDMxLCJleHAiOjE2MDU0NjY2MzF9.UuRYMdIxzc8ZFEI2z8fAgLz-LG_gDxaim25pMh9jNrDFK6YkEaDqDO8Huoav5JUB0bJyf1lTB0nNPaLLpOj4hw, refreshToken=BBF6dboG8tB4XozHqmZE5anXMHeNUncTVD8CLv2hkaU2KsfyqitlJpgkV4HrQqPk)  Repositories: [{&quot;id&quot;:&quot;{c7bb4165-92f1-4621-9039-bb1b6a74488e}&quot;,&quot;fullName&quot;:&quot;test-namespace\/test-repository1&quot;},{&quot;id&quot;:&quot;{aa149604-c136-41e1-b7bd-3088fb73f1b2}&quot;,&quot;fullName&quot;:&quot;test-namespace\/test-repository2&quot;}]  Refreshed tokens: Tokens(accessToken=eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJldm9sdmVjaS10ZXN0a2l0IiwidXNlcm5hbWUiOiJFdm9sdmVDSSBUZXN0a2l0IiwiaWF0IjoxNjA1NDY2MDM2LCJleHAiOjE2MDU0NjY2MzZ9.oR2A_9k4fB7qpzxvV5QKY1eU_8aZMYEom-ngc4Kuc5omeGPWyclfqmiyQTpJW_cHOcXbY9S065AE_GKXFMbh_Q, refreshToken=mdc5sgmtiwLD1uryubd2WZNjNzSmc5UGo6JyyzsiYsBgOpeaY3yw3T3l8IKauKYQ) <\/code><\/pre>\n<p>  <\/p>\n<h2>\u0418\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434<\/h2>\n<p>  \u041f\u043e\u043b\u043d\u044b\u0439 \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043d\u0430 <a href=\"https:\/\/github.com\/nbondarchuk\/oauth2-bitbucket-jwt-example\" rel=\"nofollow\">Github<\/a>.<\/p>\n<h2>\u0421\u0441\u044b\u043b\u043a\u0438<\/h2>\n<p>  <\/p>\n<ul>\n<li> <a href=\"https:\/\/ru.wikipedia.org\/wiki\/OAuth\" rel=\"nofollow\">OAuth \u2014 \u0412\u0438\u043a\u0438\u043f\u0435\u0434\u0438\u044f<\/a> <\/li>\n<li> <a href=\"https:\/\/docs.spring.io\/spring-security\/site\/docs\/5.2.3.RELEASE\/reference\/htmlsingle\/#oauth2login-advanced-userinfo-endpoint\" rel=\"nofollow\">Spring Security Reference<\/a> <\/li>\n<li> <a href=\"https:\/\/tools.ietf.org\/html\/rfc7519\" rel=\"nofollow\">JSON Web Token (JWT)<\/a> <\/li>\n<li> <a href=\"https:\/\/tools.ietf.org\/html\/rfc6749#section-4.1.1\" rel=\"nofollow\">The OAuth 2.0 Authorization Framework<\/a> <\/li>\n<\/ul>\n<p>  P.S.<br \/>  \u0421\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0439 \u043d\u0430\u043c\u0438 REST-\u0441\u0435\u0440\u0432\u0438\u0441 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043f\u043e \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u0443 HTTP, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0443\u0441\u043b\u043e\u0436\u043d\u044f\u0442\u044c \u043f\u0440\u0438\u043c\u0435\u0440. \u041d\u043e \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0442\u043e\u043a\u0435\u043d\u044b \u0443 \u043d\u0430\u0441 \u043d\u0438\u043a\u0430\u043a \u043d\u0435 \u0448\u0438\u0444\u0440\u0443\u044e\u0442\u0441\u044f, \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u0435\u0442\u0441\u044f \u043f\u0435\u0440\u0435\u0439\u0442\u0438 \u043d\u0430 \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u044b\u0439 \u043a\u0430\u043d\u0430\u043b (HTTPS).<\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/post\/528410\/\"> https:\/\/habr.com\/ru\/post\/528410\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"\n<div class=\"post__text post__text-html post__text_v1\" id=\"post-content-body\">\u0412 <a href=\"https:\/\/habr.com\/ru\/post\/497588\/\"> \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0439 <\/a> \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0430\u043b\u0438 \u043f\u0440\u043e\u0441\u0442\u043e\u0435 \u0437\u0430\u0449\u0438\u0449\u0435\u043d\u043d\u043e\u0435 \u0432\u0435\u0431 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0434\u043b\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0441\u044f \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b OAuth2 \u0441 Bitbucket \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438. \u041a\u043e\u043c\u0443-\u0442\u043e \u0442\u0430\u043a\u0430\u044f \u0441\u0432\u044f\u0437\u043a\u0430 \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f \u0441\u0442\u0440\u0430\u043d\u043d\u043e\u0439, \u043d\u043e \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u044c\u0442\u0435, \u0447\u0442\u043e \u043c\u044b \u0440\u0430\u0437\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c CI (Continuous Integration) \u0441\u0435\u0440\u0432\u0435\u0440 \u0438 \u0445\u043e\u0442\u0435\u043b\u0438 \u0431\u044b \u0438\u043c\u0435\u0442\u044c \u0434\u043e\u0441\u0442\u0443\u043f \u043a \u0440\u0435\u0441\u0443\u0440\u0441\u0430\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0435 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044f \u0432\u0435\u0440\u0441\u0438\u0439. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043f\u043e \u0442\u0430\u043a\u043e\u043c\u0443 \u0436\u0435 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0443 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0430\u044f CI \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430 <a href=\"https:\/\/drone.io\" rel=\"nofollow\">drone.io<\/a>.<\/p>\n<p>  \u0412 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0430\u0441\u044c HTTP-\u0441\u0435\u0441\u0441\u0438\u044f (\u0438 \u043a\u0443\u043a\u0438). \u041e\u0434\u043d\u0430\u043a\u043e \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 REST-\u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0434\u0430\u043d\u043d\u044b\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u043d\u0435 \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043e\u0434\u043d\u0438\u043c \u0438\u0437 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u0439 REST \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043e\u0442\u0441\u0443\u0442\u0441\u0432\u0438\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f. \u0412 \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c REST-\u0441\u0435\u0440\u0432\u0438\u0441, \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u044f \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043a \u043a\u043e\u0442\u043e\u0440\u043e\u043c\u0443 \u0431\u0443\u0434\u0435\u0442 \u043e\u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043b\u044f\u0442\u044c\u0441\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0442\u043e\u043a\u0435\u043d\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 (access token).  <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-313234","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/313234","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=313234"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/313234\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=313234"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=313234"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=313234"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}