در قسمت قبل مقدمه ای در مورد احراز هویت و یک روش ابتدایی آنرا توضیح دادیم در این قسمت به پیاده سازی احراز هویت از نوع Basic و Digest روی URI یکسان میپردازیم
هر دو روش از استاندارد های احراز هویت در RESTful هستند
<http create-session="stateless"> <intercept-url pattern="/api/admin/**" access="ROLE_ADMIN" /> <http-basic /> </http>
بکار بردن تگ <http-basic /> بتنهایی کافی است که BasicAuthenticationFilter و BasicAuthenticationEntryPoint را بسازد و انها را بهم مرتبط کند
تنظیمات Digest Authentication :
با تنظیمات قبلی شروع میکنیم ابتدا برای Digest Authentication نیاز است که Filter و entry point را به عنوان Bean هایی تعریف کنیم سپس digest entry point با چیزی که در تگ http-basic در پشت صحنه ساخته شده است override خواهد شد و در آخر Digest Filter مورد نظرمان بعد از basic authentication filter قرار میگیرد
<http create-session="stateless" entry-point-ref="digestEntryPoint"> <intercept-url pattern="/api/admin/**" access="ROLE_ADMIN" /> <http-basic /> <custom-filter ref="digestFilter" after="BASIC_AUTH_FILTER" /> </http> <beans:bean id="digestFilter" class= "org.springframework.security.web.authentication.www.DigestAuthenticationFilter"> <beans:property name="userDetailsService" ref="userService" /> <beans:property name="authenticationEntryPoint" ref="digestEntryPoint" /> </beans:bean> <beans:bean id="digestEntryPoint" class= "org.springframework.security.web.authentication.www.DigestAuthenticationEntryPoint"> <beans:property name="realmName" value="Contacts Realm via Digest Authentication"/> <beans:property name="key" value="acegi" /> </beans:bean> <authentication-manager> <authentication-provider> <user-service id="userService"> <user name="eparaschiv" password="eparaschiv" authorities="ROLE_ADMIN" /> <user name="user" password="user" authorities="ROLE_USER" /> </user-service> </authentication-provider> </authentication-manager>
متاسفانه Digest Authentication همانند Basic Authentication بصورت اتوماتیک تنظیماتش ست نمیشود در Basic Authentication فقط کافی بود از <http-basic> استفاده کنیم ولی در Digest نیاز داریم که Bean هایی تعریف کنیم که به تنظیمات امنیتی متصل شوند
حال چطور میتوان هر دو روش احراز هویت را در یک URI داشته باشیم :
در قبل دیدیم که هر روش بتنهایی چقدر ساده قابل تعریف بودند ولی اگر بخواهیم هر دو را در یک مسیر URI داشته باشیم یک لایه پیچیدگی به آن اضافه خواهد شد
- زمانی که درخواست مشخصی ارسال میشود :
در هدر فیلد Authentication اگر با پیشوند Basic آغاز شده بود نوع Basic و اگر با پیشوند Digest آغاز شده بود نوع Digest در نظر گرفته میشود
- زمانی که یک درخواست غیر واضح ارسال میشود :
هر دو filter زمانی که یک درخواست بدون فیلد Authorization در هدر دریافت میکنند چون برای هر دو ناشناس است بدون انجام فرآیندی در ادامه زنجیره درخواست جلو خواهد رفت و در نهایت وقتی ببیند که درخواست بدون authentication است خطای AccessDeniedException تولید و توسط ExceptionTranslationFilter گرفته میشود و کد 401 به کلاینت برای احراز هویت ارسال میشود
حال با همین سناریو و با مشخص کردن نوع احراز هویت میتوان در URI یکسان از هر دو روش بهره برد و اگر نوع authentication را مشخص نکنیم در Spring Security نوع Digest بصورت پیش فرض در نظر گرفته خواهد شد
تست هر دو روش با URI یکسان :
@Test public void givenAuthenticatedByBasicAuth_whenAResourceIsCreated_then201IsReceived(){ // Given // When Response response = given() .auth().preemptive().basic( ADMIN_USERNAME, ADMIN_PASSWORD ) .contentType( HttpConstants.MIME_JSON ).body( new Foo( randomAlphabetic( 6 ) ) ) .post( paths.getFooURL() ); // Then assertThat( response.getStatusCode(), is( 201 ) ); } @Test public void givenAuthenticatedByDigestAuth_whenAResourceIsCreated_then201IsReceived(){ // Given // When Response response = given() .auth().digest( ADMIN_USERNAME, ADMIN_PASSWORD ) .contentType( HttpConstants.MIME_JSON ).body( new Foo( randomAlphabetic( 6 ) ) ) .post( paths.getFooURL() ); // Then assertThat( response.getStatusCode(), is( 201 ) ); }
در تست دقت کنید که دفعه اول که احراز هویت انجام شد کاربر authenticate شده و در دفعه دوم نیاز است که تست را دوباره برای حالت بعدی راه اندازی کنید ولی اگر چنانچه درخواست دوم هم ارسال شد چالشی برای سرور ایجاد نمیکند چون آنرا بصورت Digest در نظر میگیرد