EJB3的宣告式安全,可以使用Annotation來宣告基於Role的授權,指明類別或哪些方法可以被哪些角色所存取。
您可以在類別上使用@DeclarRoles宣告有效的Role名稱,例如:
@Stateless
@DeclarRoles({"admin", "manager"})
public class HelloBeanImpl implements HelloBean {
....
}
@DeclarRoles({"admin", "manager"})
public class HelloBeanImpl implements HelloBean {
....
}
如果您沒有宣告有效的Role名稱,則容器也會自動根據所設定的@RolesAllowed來建立,@RolesAllowed可以套用在類別或方法上,若套用在類別上,則該類別的所有方法都必須是指定的Role才可以存取,若套用在方法上,則該方法必須是指定的Role才可以存取,例如:
@Stateless
@DeclarRoles({"admin", "manager"})
public class HelloBeanImpl implements HelloBean {
@RolesAllowed("admin")
public void doAdmin() {
...
}
...
}
@DeclarRoles({"admin", "manager"})
public class HelloBeanImpl implements HelloBean {
@RolesAllowed("admin")
public void doAdmin() {
...
}
...
}
您也可以使用@PermitAll或@DenyAll來標註整個類別或方法,標註@PermitAll表示可以被任何Role使用呼叫,@DenyAll則是相反,表示任何Role都不得使用呼叫,在某些環境中,不適合呼叫某些方法或類別時,您就可以使用@DenyAll來設定,而不用直接修改程式碼。
您可以使用@RunAs標註類別或方法,在執行時A Role的方法中,若呼叫了某些B Role才能呼叫的方法,則可暫時以B Role來執行,例如:
@Stateless
@DeclarRoles({"admin", "manager"})
@RunAs("admin")
public class HelloBeanImpl implements HelloBean {
@RolesAllowed("admin")
public void doAdmin() {
...
}
@RolesAllowed("admin")
public void doAdmin() {
...
}
...
...
@RolesAllowed("manager")
public void doManager() {
...
doAdmin(); // 需要 admin 的 Role 才能執行
...
}
...
}
@DeclarRoles({"admin", "manager"})
@RunAs("admin")
public class HelloBeanImpl implements HelloBean {
@RolesAllowed("admin")
public void doAdmin() {
...
}
@RolesAllowed("admin")
public void doAdmin() {
...
}
...
...
@RolesAllowed("manager")
public void doManager() {
...
doAdmin(); // 需要 admin 的 Role 才能執行
...
}
...
}
manager的Role執行doManager(),由於其中執行了admin的Role才可以執行的方法,若沒有設定如上的@RunAs,則會發生錯誤。
Java EE的安全是基於JAAS(Java Authentication and Authorization Service),若為Web應用程式,可以搭配Web容器的URL Pattern為基礎的宣告安全設定,當使用者登入後,Web容器會將通過驗證的Principal傳遞給EJB容器,可以很簡單的完成驗證與EJB3上 的宣告授權。若不透過Web容器的宣告安全設定而想使用EJB3宣告授權,需要深入了解JAAS的內容,這已不在這個文件的說明範圍。
以下配合Web容器的 宣告式基本驗證,讓Web容器為您作驗證,並在Servlet中呼叫EJB3的Bean,看看如何使用EJB3的宣告安全,首先是Bean的撰寫:
- HelloBean.java
package onlyfun.caterpillar;
import javax.ejb.Remote;
@Remote
public interface HelloBean {
public String doHello(String message);
public String doSecurity(String message);
}
- HelloBeanImpl.java
package onlyfun.caterpillar;
import javax.annotation.security.*;
import javax.ejb.Stateless;
@Stateless
public class HelloBeanImpl implements HelloBean {
@RolesAllowed("foo")
public String doSecurity(String message) {
return message + "security processed....";
}
public String doHello(String message) {
return message + "processed....";
}
}
其中doSecurity()必須是foo的Role才可以存取,其它方法則無限制,若您撰寫一個Servlet如下:
- HelloServlet.java
package onlyfun.caterpillar;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.ejb.EJB;
public class HelloServlet extends HttpServlet {
@EJB
private HelloBean hello;
protected void processRequest(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
String method = request.getParameter("method");
if("security".equals(method)) {
response.getWriter().println(hello.doSecurity("info...."));
}
else {
response.getWriter().println(hello.doHello("hello...."));
}
}
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
public String getServletInfo() {
return "Short description";
}
}
這個Servlet使用Web的安全宣告加以保護,只能是foo或orz的Role才可以請求:
- web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>onlyfun.caterpillar.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/HelloServlet</url-pattern>
</servlet-mapping>
<security-role>
<role-name>foo</role-name>
</security-role>
<security-role>
<role-name>orz</role-name>
</security-role>
<security-constraint>
<display-name>SecurityConstraint</display-name>
<web-resource-collection>
<web-resource-name>Secret Information</web-resource-name>
<url-pattern>/HelloServlet</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>foo</role-name>
<role-name>orz</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>file</realm-name>
</login-config>
</web-app>
若您的Glassfish上有caterpillar及justin使用者,群組分別設為fooGroup與orzGroup,而sun-web.xml的設定如下:
...
<security-role-mapping>
<role-name>foo</role-name>
<group-name>fooGroup</group-name>
</security-role-mapping>
<security-role-mapping>
<role-name>orz</role-name>
<group-name>orzGroup</group-name>
</security-role-mapping>
...
<security-role-mapping>
<role-name>foo</role-name>
<group-name>fooGroup</group-name>
</security-role-mapping>
<security-role-mapping>
<role-name>orz</role-name>
<group-name>orzGroup</group-name>
</security-role-mapping>
...
若您請求/HelloServlet,並指定method=security參數,當使用caterpillar登入時,才可以呼叫HelloBean的doSecurity(),若使用justin登入,雖可以通過驗證來請求,但因Role不正確,所以呼叫HelloBean的doSecurity()時,就會出現授權失敗的錯誤。
若不指定method=security參數,則呼叫的是HelloBean的doHello(),則無論登入的是caterpillar或justin,都可以正確執行。