১। ভূমিকা
স্প্রিং সিকিউরিটি জাভা অ্যাপ্লিকেশনের নিরাপত্তা দেয়ার জন্য একটা ফ্রেমওয়ার্ক। এরকম আরও কয়েকটা ফ্রেমওয়ার্ক আছে যেমন PicketLink, Apache Shiro, OOAC ইত্যাদি কিন্তু এর মধ্যে স্প্রিং সিকিউরিটিই সবচেয়ে বেশী জনপ্রিয়, এবং একটা ওয়েব অ্যাপের সিকিউরিটি দেয়ার জন্য য কিছু দরকার তার প্রায় সব কিছুই এই ফ্রেমওয়ার্কে পাওয়া যাবে। এবং শেখাটাও খুব সহজ।
এই আর্টিকেলে আমরা স্প্রিং সিকিউরিটি ফ্রেমওয়ার্ক দিয়ে একটা জাভা অ্যাপ্লিকেশনের বেসিক সিকিউরিটি ইমপ্লিমেন্ট করার চেষ্টা করবো। সিকিউরিটি কনফিগারেশন দুই উপায়ে আমরা লিখতে পারি। XML এবং জাভা কনফিগারেশন। এখানে জাভা কনফিগারেশনে বেসিক অথেনটিকেশন দেখানো হবে, যেহেতু Spring 3.5 থেকেই আমরা @Annotation বেইজড কনফিগারেশন লিখতে পারি এবং স্প্রিং বুটে সম্পূর্নই জাভা কনফিগারেশন ইউজ করা হয় কাজেই XML কনফিগারেশন অনেকটা আনঅফিসিয়ালি ডেপ্রিকেটেড।
একটা বেসিক লগইন মেকানিজম দিয়ে আমাদের বেসিক সিকিউরিটি সিম্যুলেট করে দেখানো সহজ হবে। তার মানে হচ্ছে ওয়েব অ্যাপে এক্সেস করার জন্য ইউজারের একটা ইউজারনেম/ইমেইল এবং পাসওয়ার্ডের প্রয়োজন হবে।
২। Maven/Gradle ডিপেন্ডেন্সি
স্প্রিং সিকিউরিটির জন্য সর্বপ্রথম আমাদেরকে স্প্রিং সিকিউরিটির ডিপেন্ডেন্সি ক্লাসপাথে অ্যাড করে নিতে হবে। স্প্রিং বুটে অ্যাপের জন্য এক্ষেত্রে আমি spring-boot-starter-security অ্যাড করছি।
Maven:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
Gradle:
compile group: 'org.springframework.boot', name: 'spring-boot-starter-security', version: '1.5.2.RELEASE'
আপনার অ্যাপটি যদি স্প্রিং বুট দিয়ে ডেভেলপ করা হয়ে থাকে তবে শুধুমাত্র এই ডিপেন্ডেন্সি যুক্ত করলেই আপনার অ্যাপলিকেশনটিতে স্প্রিং সিকিউরিটি তার হিরোইজম দেখানো শুরু করবে। Spring MVC অ্যাপ হলে আপনাকে web.xml ফাইলে springSecurityFilterChain নামে একটা ফিল্টার বসাতে হবে যার ক্লাস হবে org.springframework.web.filter.DelegatingFilterProxy। এবং একটা ফিল্টার ম্যাপিং। কনফিগারেশনটা হবে ঠিক এইরকমঃ
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
-
স্প্রিং সিকিউরিটি কিভাবে কাজ করে
একটা অ্যাপ্লিকেশনের সিকিউরিটি দেয়ার সময় আপনি দুইটা মেথডোলজি অনুসরণ করতে পারেন। একটা হচ্ছে ডিফল্টভাবে আপনার অ্যাপের সব রিসোর্স উন্মুক্ত রেখে আপনি যেসব রিসোর্সে ইউজারের এক্সেসের ক্ষেত্রে অথেন্টিসিটি দাবি করবেন সেগুলাতে সিকিউরিটি দেয়া। আরেকটা উপায় হচ্ছে অ্যাপ পুরোপুরি সিকিউরড করে অথেন্টিকেটেড ইউজারের জন্য অথোরাইজেশন অনুযায়ী রিসোর্স খুলে দেয়া। প্রথম পদ্বতিটা খুব রিস্কি। কেননা এক্ষেত্রে আপনাকে অনেকগুলো রিকোয়েস্টের জন্য অনেকগুলো কম্বিনেশন চিন্তা করতে হবে, খুঁজে বের করতে হবে আর কোথায় কোথায় অথেনটিকেশন দেয়া লাগবে। কোন রিসোর্সে সিকিউরিটি দিতে ভুলে গেলেই সর্বনাশ! এদিকে দ্বিতীয় পদ্মতিটা নিরাপদ কেননা এক্ষেত্রে আপনার পুরো অ্যাপ্লিকেশন সিকিউড আগে থেকেই। আপনি এক্ষেত্রে চিন্তা করছেন কাকে আর কোথায় এক্সেস দেয়া প্রয়োজন। সে অনুযায়ী অথোরাইজেশন সেট করে দেবেন। স্প্রিং সিকিউরিটি ফিলটার চেইন আপনাকে আগে থেকেই অ্যাপের অ্যাক্সেস ব্লক করে দিচ্ছে। আপনার কাজ হচ্ছে ব্লক খুলে খুলে অথোরাইজেশন দিয়ে দেয়া।
-
স্প্রিং সিকিউরিটি জাভা কনফিগারেশন
আপনি একটা স্প্রিং এর বিন তৈরি করবেন @Configuration Annotation দিয়ে, যেটাতে WebSecurityConfigurerAdapter ক্লাসটা এক্সটেন্ড করতে হবে। @Configuration অ্যানোটেশন দেয়ার কারনে স্প্রিং এই ক্লাসটাকে কনফিগারেশন বিন হিসেবে রেজিস্টার করে নেবে।
WebSecurityConfigurerAdapter ক্লাসের বেশ কিছু মেথড আছে যেগুলা আমরা Override করতে পারবো। তার মধ্যে আমরা এখানে আপাতত তিনটা মেথড Override করবো।
void configure(WebSecurity web)
void configure(HttpSecurity http)
void configure(AuthenticationManagerBuilder auth)
মেথড তিনটার চেহারা প্রায় একই হলে কাজ কিছুটা ভিন্ন। প্রথম মেথডটা সাধারণত ওয়েব রিসোর্সগুলাকে স্প্রিং সিকিউরিটির আওতার বাইরে ফেলার জন্য ব্যাবহার করা হয়। এই ধরেন html, css কিংবা javascript ফাইলগুলা যাদের সিকিউরিটি দিতে গেলে শুধু শুধু অ্যাপের চেহারা খারাপ হবে কিংবা ইউজার অন্ধ হয়ে যাবে, অর্থাৎ কিছু দেখতে পাবে না আর কি।
দ্বিতীয় মেথডটা ওভাররাইড করে কাকে, কখন, কিভাবে, কোথায় ঢোকার অনুমতি দেয়া যায় অর্থাৎ অথোরাইজেশন করা হবে এবং সর্বশেষ মেথডটা ইউজারকে অথেন্টিকেট অর্থাৎ ইউজার আসলেই সে কিনা সেটা যাচাই করা হবে।
জাভা কনফিগারেশটা দেখতে এরকম হবে
@Configuration
public class SecurityConfigAdapter extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/resources/**", "/fonts/**");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests() .antMatchers("/questions/**").hasRole("USER”)
.antMatchers("/admin/**").hasAnyRole("ADMIN", "SUPER_ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/login")
.failureUrl("/login?error")
.permitAll();
http
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login")
.deleteCookies("JSESSIONID")
.permitAll();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("admin").password("password").roles("USER"); }
}
কনফিগারেশনের এই সুন্দর চেহারা দেখে জাভা ডেভেলপারদের একটু ধন্ধে পড়ে যাওয়ার সম্ভাবনা আছে বিশেষ করে যাদের ফাংশনাল প্রোগ্রামিং প্যারাডাইম সম্পর্কে খুব একটা আইডিয়া নাই। তবে এটা খুবই ক্লিন এবং রিডেবল। ফাংশনাল প্রোগ্রামিং সম্পর্কে বিস্তারিত জানতে এখানে দেখতে পারেন।
এখন আসি এখানে কিভাবে কি করা হয়েছে সেটা নিয়ে। প্রথম মেথডটাতে আমরা যা করলাম তার অর্থ হচ্ছে আমরা আমাদের /resource ফোল্ডারে যত ফাইল আছে সব কিছু সকল ইউজারের জন্য উন্মুক্ত করে দিলাম। কারন আপনি হয়তো আপনার হোমপেইজে সব মানুষকে নিমন্ত্রণ জানাবেন, তারা লগইন না করেই আপনার ওয়েবসাইট নেড়েচেড়ে দেখবে, আপনার অ্যাপ সম্পর্কে তথ্য নেবে, কিন্তু রিসোর্স ফোলাডারের html/css/js ফাইলগুলোতে অ্যাক্সেস না থাকলে কিন্তু স্প্রিং ইউজারকে পুরোপুরি আটকে দেবে। দেখা যাবে লগইন পেইজে ঢোকার জন্য ইউজারকে লগইন পেইজ দেখাতে যাবে, আবার সেই লগইন পেইজ দেখানোর জন্য আবার লগইন পেইজ.. এভাবে ইনিফাইনাইট লুপ ঘুরতে থাকবে।
এজন্যই .permitAll() মেথড দিয়ে কিছু রিসোর্স কিংবা রিকোয়েস্টকে সকল ভিজিটরের জন্য উন্মুক্ত করে দেয়া হয়েছে void configure(HttpSecurity http) মেথডে।
এখানে .and() মেথড XML কনফিগারেশনের একটা element এর সমাপ্তি হিসেবে কল্পনা করতে পারেন।
.authorizeRequests() দিয়ে বলা হচ্ছে সব ধরনের রিকোয়েস্ট অথরাইজ করা হবে। প্রথমেই সবাইকে আটকে দিয়ে .antMatchers() দিয়ে নির্দিষ্ট ইউআরএল এ নির্দিষ্ট রোলের ইউজারকে অ্যাক্সেস দেয়া হয়েছে।
আমরা এখানে আপাতত ইন মেমোরি অথেনটিকেশন ব্যাবহার করেছি শেষের মেথডটিতে। পরবর্তী আর্টিকেলে userDetailsService দিয়ে (ডেটাবেইজে ইউজারের টেবিল থেকে তথ্য নিয়ে) ইউজার অথেনটিকেশন এবং অথরাইজ দেখানোর চেষ্টা করবো।
-
XML কনফিগারেশন
XML কনফিগারেশন নিয়ে বেশী কিছু বলবো না। উপরের জাভা কনফিগারেশন কিভাবে কাজ করে বুঝতে পারলে XML কনফিগারেশনের কোড দেখলে বুঝতে পারবেন আশা করছি।
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-4.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd">
<http use-expressions="true">
<intercept-url pattern="/login*" access="isAnonymous()" />
<intercept-url pattern="/**" access="isAuthenticated()"/>
<form-login
login-page='/login.html'
default-target-url="/homepage.html"
authentication-failure-url="/login.html?error=true" />
<logout logout-success-url="/login.html" />
</http>
<authentication-manager>
<authentication-provider>
<user-service>
<user name="user1" password="user1Pass" authorities="ROLE_USER" />
</user-service>
</authentication-provider>
</authentication-manager>
</beans:beans>
পরবর্তীঃ UserDetailsService দিয়ে ইউজার অথেনটিকেশন দেখুন এই আর্টিকেলে।