如何建立Spring Boot Security專案並以夾帶form-data的api登入LDAP認證。
本範例是以Spring Boot + Spring Security + Spring LDAP + Gradle 驗證範例來修改。
將原本以預設form表單畫面登入的方式,改為由api帶form-data的方式來認證,請參考Spring Boot Security form api login範例。
Spring Security配置類LdapWebSecurityConfig
修改後的內容如下,此為本篇重點。
LdapWebSecurityConfig
package com.abc.demo.config;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.LdapShaPasswordEncoder;
@EnableWebSecurity
public class LdapWebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginProcessingUrl("/login")
.usernameParameter("username")
.passwordParameter("password")
.successHandler( new DemoAuthenticationSuccessHandler() )
.failureHandler( new DemoAuthenticationFailureHandler() )
.and()
.exceptionHandling()
.authenticationEntryPoint( new DemoAuthenticationEntryPoint() )
.and()
.csrf()
.ignoringAntMatchers("/login");
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.ldapAuthentication()
.userDnPatterns("uid={0},ou=people")
.groupSearchBase("ou=groups")
.contextSource()
.url("ldap://localhost:8389/dc=springframework,dc=org")
.and()
.passwordCompare()
.passwordEncoder(new LdapShaPasswordEncoder())
.passwordAttribute("userPassword");
}
}
然後建立在LdapWebSecurityConfig
需要引入的類別如下。
自訂請求返回結果的資料傳輸物件類別(DTO)DemoResponse
。
DemoResponse
package com.abc.demo.dto;
import org.springframework.http.HttpStatus;
public class DemoResponse {
private Integer status;
private String message;
public DemoResponse() {
this.status = HttpStatus.OK.value();
this.message = "success";
}
public DemoResponse(Integer status, String message) {
this.status = status;
this.message = message;
}
// getters and setters
}
建立驗證成功處理器DemoAuthenticationSuccessHandler
。
DemoAuthenticationSuccessHandler
package com.abc.demo.config;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import com.abc.demo.dto.DemoResponse;
import com.fasterxml.jackson.databind.ObjectMapper;
public class DemoAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
response.getOutputStream().println(new ObjectMapper().writeValueAsString(new DemoResponse()));
}
}
建立驗證失敗處理器DemoAuthenticationFailureHandler
。
DemoAuthenticationFailureHandler
package com.abc.demo.config;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.HttpStatus;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import com.abc.demo.dto.DemoResponse;
import com.fasterxml.jackson.databind.ObjectMapper;
public class DemoAuthenticationFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response,
org.springframework.security.core.AuthenticationException exception)
throws IOException, ServletException {
response.setStatus(HttpStatus.FORBIDDEN.value());
DemoResponse data = new DemoResponse(HttpStatus.FORBIDDEN.value(),exception.getMessage());
response.getOutputStream().println(new ObjectMapper().writeValueAsString(data));
}
}
建立未通過驗證請求資源時的返回設定DemoAuthenticationEntryPoint
。
DemoAuthenticationEntryPoint
package com.abc.demo.config;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import com.abc.demo.dto.DemoResponse;
import com.fasterxml.jackson.databind.ObjectMapper;
public class DemoAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
DemoResponse data = new DemoResponse(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.name());
response.getOutputStream().println(new ObjectMapper().writeValueAsString(data));
}
}
application.properties
設定專案的context path路徑為/demo
,加上原本Embedded LDAP Server設定如下。
application.properties
#應用程式context path
server.servlet.context-path=/demo
#LDAP人員資料來源位置
spring.ldap.embedded.ldif=classpath:test-server.ldif
#LDAP base dn
spring.ldap.embedded.base-dn=dc=springframework,dc=org
#LDAP Server port
spring.ldap.embedded.port=8389
範例專案結構目錄如下。
完成以上後即可啟動專案來用Postman測試。
開啟Postman,在uri欄位輸入http://localhost:8080/demo/login
,HTTP method為POST
。
在Body的form-data輸入表示帳號密碼的key/value。
KEY | VALUE |
---|---|
username | bob |
password | bobspassword |
按Send送出,返回結果如下代表通過驗證。
{"status":200,"message":"success"}
通過驗證後可以用GET
來傳送http://localhost:8080/demo/
,返回結果如下。
Welcome to the home page!
LdapShaPasswordEncoder似乎被棄用了,請問有適合的encoder嗎?
回覆刪除DelegatingPasswordEncoder
回覆刪除