基於CAS 實現與容器(Tomcat 或者 JBoss)的SSO
[b]需求描述1[/b]:大家知道J2EE應用程式都可以用型別以下形式進行保護:
而使用CAS實現SSO之後,我們又不想容器保護去掉,該怎麼辦才能真正把這個CAS驗證過的使用者和角色傳給Tomcat容器,而不至於重新登入,或者出現403錯誤呢?
[b]需求描述2[/b]: 在有EJB資源的時候,我們通常都會用JAAS來實現保護EJB資源,以限制EJB資源的訪問。在呼叫EJB的時候,需要輸入使用者名稱和密碼並且此使用者具有相應的角色才能呼叫EJB。而在實現SSO之後,如果真正實現與EJB容器進行SSO呢?
[b]
下面基於Tomcat容器實現需求1.[/b]
1. Tomcat視窗提供了一個叫Valve的介面,還提供一個基本實現ValveBase,我們解決方案就是基於實現一個自己的Valve, 在Valve裡把經過CAS驗證的使用者傳給Tomcat。以下就是自定義的Valve:
2. 實現好自己的Valve,在server.xml裡配置一下。然後就可以測試了。
[b]下面講實現需求2實現的思路:[/b]
1. 如果是EJB容器是JBoss,而Web容器是Tomcat, 也可以在Valve裡用SecurityAssociation類(或者其他的方法)把CAS驗證過的使用者傳到EJB容器裡。這裡同時需要傳遞密碼。
2. 要實現一個LoginModule, 類似以下程式碼:
這裡面最重要的就是commit方法了。由於這是個有點麻煩,只是在產品裡的程式碼改了一下。不保證這個LoginModule能執行正確,不過思路和基本方法都是正確的。
3. 然後在JBoss裡的配置使用這個LoginModule, 並且讓它來保護EJB資源。這樣就可以了。至於怎麼設定這個,非常繁瑣,這裡就不講了。
[b]重要注意[/b]:這裡為了演示,把密碼寫作“password”, 在實現應用中,用這麼簡單的密碼不合適,這誰都知道。但是是否使用真正的密碼,也是要考慮的。如果使用真正的密碼,在loginmodule還要驗證一次。如果使用一定邏輯來實現密碼驗證(如使用CAS的Ticket),同時也支援真正密碼登入,這樣適應性更強,效能也更好。
<login-config>
<auth-method>FORM</auth-method>
<form-login-config>
<form-login-page>/login.jsp</form-login-page>
<form-error-page>/failure.jsp</form-error-page>
</form-login-config>
<realm-name>Security Realm</realm-name>
</login-config>
<security-constraint>
<display-name>Tomcat security constraint</display-name>
<web-resource-collection>
<web-resource-name>Protected Resources</web-resource-name>
<url-pattern>/security/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>manager</role-name>
</auth-constraint>
</security-constraint>
<security-role>
<role-name>manager</role-name>
</security-role>
而使用CAS實現SSO之後,我們又不想容器保護去掉,該怎麼辦才能真正把這個CAS驗證過的使用者和角色傳給Tomcat容器,而不至於重新登入,或者出現403錯誤呢?
[b]需求描述2[/b]: 在有EJB資源的時候,我們通常都會用JAAS來實現保護EJB資源,以限制EJB資源的訪問。在呼叫EJB的時候,需要輸入使用者名稱和密碼並且此使用者具有相應的角色才能呼叫EJB。而在實現SSO之後,如果真正實現與EJB容器進行SSO呢?
[b]
下面基於Tomcat容器實現需求1.[/b]
1. Tomcat視窗提供了一個叫Valve的介面,還提供一個基本實現ValveBase,我們解決方案就是基於實現一個自己的Valve, 在Valve裡把經過CAS驗證的使用者傳給Tomcat。以下就是自定義的Valve:
package edu.extcas.valve;
import java.io.*;
import java.security.Principal;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.catalina.Container;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.realm.GenericPrincipal;
import org.apache.catalina.valves.ValveBase;
//import com.ttg.customagent.jboss.SimplePrincipal;
public class ExtCASValve extends ValveBase {
public final static String CAS_FILTER_USER = "edu.yale.its.tp.cas.client.filter.user";
public void setContainer(Container container) {
super.setContainer(container);
}
public void invoke(Request request, Response response) throws IOException,
ServletException {
HttpSession session = ((HttpServletRequest) request).getSession();
if (session != null) {
String username = (String) session.getAttribute(CAS_FILTER_USER);
if (null != username) {
List roleList = getRolesFromUserStore(username);
Principal principal = new GenericPrincipal(request.getContext()
.getRealm(), username, "", roleList);
request.setUserPrincipal(principal);
}
//SecurityAssociation類是在登入EJB的時候使用的。
//SecurityAssociation.setPrincipal(new SimplePrincipal(username.trim()));
//SecurityAssociation.setCredential("password".trim().toCharArray());
getNext().invoke(request, response);
return;
} else {
getNext().invoke(request, response);
return;
}
}
//此方法是為在使用者儲存裡取到相應的角色。自已根據實際情況實現
private List getRolesFromUserStore(String username) {
List roleList = new ArrayList();
roleList.add("admin");
roleList.add("manager");
return roleList;
}
}
2. 實現好自己的Valve,在server.xml裡配置一下。然後就可以測試了。
[b]下面講實現需求2實現的思路:[/b]
1. 如果是EJB容器是JBoss,而Web容器是Tomcat, 也可以在Valve裡用SecurityAssociation類(或者其他的方法)把CAS驗證過的使用者傳到EJB容器裡。這裡同時需要傳遞密碼。
2. 要實現一個LoginModule, 類似以下程式碼:
package edu.extcas.loginmodule;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.security.Principal;
import java.security.cert.X509Certificate;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
import org.apache.catalina.realm.GenericPrincipal;
public class ExtCasLoginModule implements LoginModule {
private CallbackHandler callbackHandler;
private Principal unauthenticatedIdentity;
private String userName = null;
private String password = null;
protected Subject subject;
protected boolean loginOk;
protected Principal identity;
public void initialize(Subject subject, CallbackHandler callbackHandler,
Map sharedState, Map options) {
this.subject = subject;
this.callbackHandler = callbackHandler;
try {
String name = (String) options.get("unauthenticatedIdentity");
if (name != null) {
unauthenticatedIdentity = createIdentity(name, "", null);
}
}
catch (Exception e) {
}
}
public boolean login() throws LoginException {
loginOk = false;
if (userName == null && password == null) {
identity = unauthenticatedIdentity;
}
if ((identity == null) && (password != null)) {
if ("password".equals(password)) {
identity = createIdentity(userName, "", null);
loginOk = true;
return true;
}
}
}
public boolean abort() throws LoginException {
subject.getPrincipals().clear();
return true;
}
public boolean logout() throws LoginException {
Principal identity = getIdentity();
Set principals = subject.getPrincipals();
if (principals.contains(identity) == true)
principals.remove(identity);
return true;
}
private Principal getIdentity() {
return identity;
}
private Principal createIdentity(String username) throws Exception {
return new CustomPrincipal(username);
}
public boolean commit() throws LoginException {
if (loginOk == false)
return false;
StringBuffer sb = new StringBuffer();
List roles = getRolesFromUserStore(userName);
Set principals = subject.getPrincipals();
if (!principals.contains(identity))
principals.add(identity);
Group jbossroles = new SimpleGroup("Roles");
for (Iterator i = roles.iterator(); i.hasNext();) {
String roleStr = (String) i.next();
jbossroles.addMember(new SimplePrincipal(roleStr));
}
principals.add(jbossroles);
return true;
}
private List getRolesFromUserStore(String username) {
List roleList = new ArrayList();
roleList.add("admin");
roleList.add("manager");
return roleList;
}
}
這裡面最重要的就是commit方法了。由於這是個有點麻煩,只是在產品裡的程式碼改了一下。不保證這個LoginModule能執行正確,不過思路和基本方法都是正確的。
3. 然後在JBoss裡的配置使用這個LoginModule, 並且讓它來保護EJB資源。這樣就可以了。至於怎麼設定這個,非常繁瑣,這裡就不講了。
[b]重要注意[/b]:這裡為了演示,把密碼寫作“password”, 在實現應用中,用這麼簡單的密碼不合適,這誰都知道。但是是否使用真正的密碼,也是要考慮的。如果使用真正的密碼,在loginmodule還要驗證一次。如果使用一定邏輯來實現密碼驗證(如使用CAS的Ticket),同時也支援真正密碼登入,這樣適應性更強,效能也更好。
相關文章
- 基於CAS的WEB單點登入(sso)服務端及其tomcat/nginx https配置Web服務端TomcatNginxHTTP
- 關於tomcat與jboss的快取Tomcat快取
- 如何在TOMCAT中實現SSO?Tomcat
- CAS實現單點登入SSO執行原理探究(終於明白了)
- 前端需要了解的SSO與CAS知識前端
- 前端需要了解的 SSO 與 CAS 知識前端
- CAS實現單點登入SSO執行原理探究
- Liferay中使用CAS實現單點登陸(SSO)
- Tomcat,JBoss與JBossWebTomcatWeb
- CAS單點登入(SSO)實戰(一)
- 基於Oauth2.0實現SSO單點認證OAuth
- 盤點JAVA中基於CAS實現的原子類, 你知道哪些?Java
- 淺析CAS操作與JAVA實現Java
- 關於安裝jboss+tomcat的問題Tomcat
- 【實踐篇】基於CAS的單點登入實踐之路
- 2.基於CAS SSO單點登入服務端環境搭建+架構原始碼服務端架構原始碼
- 2.基於CAS SSO單點登入服務端環境搭建架構原始碼服務端架構原始碼
- 不攔截Request!基於WKWebView的API實現Hybrid容器WebViewAPI
- 基於DevOps的容器安全實踐dev
- CAS SSO單點登入框架學習框架
- 基於IdentityServer4的OIDC實現單點登入(SSO)原理簡析IDEServer
- CAS (4) —— CAS瀏覽器SSO訪問順序圖詳解(CAS Web Flow Diagram by Example)瀏覽器Web
- 分散式鎖與實現(一)基於Redis實現!分散式Redis
- 跟我一起動手實現Tomcat(二):實現簡單的Servlet容器TomcatServlet
- IT轉型與基於雲的架構實現架構
- 基於CAS操作的非阻塞演算法演算法
- 樂觀的併發策略——基於CAS的自旋
- .關於CAS SSO單點登入服務端環境搭建原始碼分析服務端原始碼
- 關於CAS SSO單點登入客戶端環境搭建原始碼分析客戶端原始碼
- 基於容器雲的微服務架構實踐微服務架構
- CAS跨域SSO例項安裝和配置指南跨域
- CAS (3) —— Mac下配置CAS客戶端經代理訪問Tomcat CASMac客戶端Tomcat
- CAS (1) —— Mac下配置CAS到Tomcat(服務端)MacTomcat服務端
- CAS (2) —— Mac下配置CAS到Tomcat(客戶端)MacTomcat客戶端
- 模擬實現 Tomcat 的核心模組:NIO,HTTP,容器和叢集TomcatHTTP
- 申通的雲原生實踐之路:如何實現應用基於容器的微服務改造?微服務
- 單點登入 SSO 的實現原理
- 基於keras的BiLstm與CRF實現命名實體標註KerasCRF