網頁

2019/9/27

Spring Security LDAP form api login auth認證

如何建立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。

KEYVALUE
usernamebob
passwordbobspassword

Send送出,返回結果如下代表通過驗證。

{"status":200,"message":"success"}



通過驗證後可以用GET來傳送http://localhost:8080/demo/,返回結果如下。

Welcome to the home page!



2 則留言:

  1. LdapShaPasswordEncoder似乎被棄用了,請問有適合的encoder嗎?

    回覆刪除