簡體   English   中英

使用 JAAS 登錄模塊注銷

[英]Logout with JAAS login module

問題比預期的要長一點。 下面是一個類似的鏈接(第 3 篇文章),我沒有找到令人滿意的答案。

TL; 博士

我正在嘗試使用 JAAS 登錄模塊注銷。 下面是項目的簡要結構: LoginService負責在用戶想要登錄時實例化LoginContext

@Service
public class LoginService {
        
    public UserDTO getUserDTOFrom(Credentials credentials) {
        try {
            LoginContext loginContext = new LoginContext("Login", new JAASCallbackHandler(credentials));
            loginContext.login();
            // construct UserDTO object.
        } catch (LoginException e) {
            LOGGER.error("Login Exception: {}", e.getMessage());
            // construct UserDTO object.
        }
    // return UserDTO object.
}

LoginController調用方法:

@RestController
@RequestMapping("/login")
public class LoginController {
    
    private final LoginService loginService;
    
    @Autowired
    public LoginController(LoginService loginService) {
        this.loginService = loginService;
    }
    
    @PostMapping
    public ResponseEntity<UserDTO> getUserDTOFrom(@Valid @RequestBody Credentials credentials) {
        UserDTO userDTO = loginService.getUserDTOFrom(userForm);
        // return response that depends on outcome in the login service
    }
}

當我想注銷以前登錄的用戶時會出現問題。 LoginContext負責調用 JAAS 登錄模塊中的注銷方法。 例如:

loginContext.logout();

JAAS登錄模塊中的方法:

public class JAASLoginModule implements LoginModule {
    
    @Override
    public boolean logout() {
        subject.getPrincipals().remove(usernamePrincipal);
        subject.getPrincipals().remove(passwordPrincipal);
        return true;
    }
}

我在LogoutService沒有LoginContext並且無法完全清除以前經過身份驗證的主題。

我試圖創建一個單例 bean 來獲取LoginContext的相同實例:

@Configuration
public class LoginContextBean {
    
    @Lazy
    @Bean
    public LoginContext getLoginContext(Credentials credentials) throws LoginException {
        System.setProperty("java.security.auth.login.config", "resources/configuration/jaas.config");
        return new LoginContext("Login", new JAASCallbackHandler(credentials));
    }
}

@Service
public class LoginService {
    
    private final ObjectProvider<LoginContext> loginContextProvider;
    
    @Autowired
    public LoginService(ObjectProvider<LoginContext> loginContextProvider) {
        this.loginContextProvider = loginContextProvider;
    }
    
    public UserDTO getUserDTOFrom(Credentials credentials) {
        try {
            LoginContext loginContext = loginContextProvider.getObject(credentials);
            loginContext.login();
            // construct UserDTO object.
        } catch (LoginException e) {
            LOGGER.error("Login Exception: {}", e.getMessage());
            // construct UserDTO object.
        }
    // return UserDTO object.
    }
}

@Service
public class LogoutService {
    
    private final ObjectProvider<LoginContext> loginContextProvider;
    
    @Autowired
    public LogoutService(ObjectProvider<LoginContext> loginContextProvider) {
        this.loginContextProvider = loginContextProvider;
    }
    
    public void performLogout() {
        LoginContext loginContext = loginContextProvider.getObject();
        try {
            loginContext.logout();
        } catch (LoginException e) {
            LOGGER.error("Failed to logout: {}.", e.getMessage());
        }
    }
}

該解決方案不是特別有用,因為下一個/同一用戶登錄將在LoginContext上獲得 NPE。 我讀到HttpServletRequestgetSession().invalidate(); 假設調用 JAAS 的logout()HttpServletRequestlogout()將完成這項工作。 但是這兩種方法都沒有效果。 例如:

@RestController
@RequestMapping("/logout")
public class LogoutController {
    
    private final LogoutService logoutService;
    
    @Autowired
    public LogoutController(LogoutService logoutService) {
        this.logoutService = logoutService;
    }
    
    @DeleteMapping
    public ResponseEntity<Void> deleteJwt(@CookieValue("jwt_cookie") String jwtToken, HttpServletRequest request) throws ServletException {
        request.getSession().invalidate(); // logout() is not called.
        request.logout(); // logout() is not called.
        return getResponse();
    }
} 

我想在用戶想要注銷時使用之前創建的LoginContext ,但在另一個用戶嘗試登錄時創建一個新的LoginContext 。請注意,我沒有使用 Spring Security。

編輯

其中一個想法是使用一個單例來保存一Set與特定用戶關聯的登錄上下文。 然后在用戶注銷時調用並銷毀它們。 此類Set鍵可以是 JWT 令牌或用戶 ID。 經過進一步思考,在我看來,一個用戶可能有多個會話,在這種情況下,用戶 ID 作為密鑰將無法達到其目的。 第二個選項是 JWT 令牌,但有一種情況,未來的中間件將在到期時發出新的 JWT 令牌,然后我的Set將無法返回有效的登錄上下文。

經過一些研究,我的團隊認為 JAAS 不適合我們的需求。 我們沒有使用它提供的完整功能,它束縛了我們的手,而不是使開發過程順暢。

如果您會遇到類似的問題,這里有一個解釋:我們使用的是支持 JAAS 的 WebSphere 8.5.5。 可以注銷,但代價是將其綁定到應用程序服務器。 考慮到我們的計划是從 WebSphere 遷移,此實現不是一個選項。
此類指南之一的鏈接位於此處

未來有兩種選擇:

  1. 將其包裝在 Spring Security 中,因為它提供對 JAAS 的支持
  2. 完全依賴 Spring Security 的功能替換自定義模塊。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM