Посмотрим правде в глаза коллеги разработчики, наши приложения имеют тенденцию расти. И особенно с новыми функциями и модными функциями. И некоторые из них должны быть скрытыми, если пользователи не имеют привилегии видеть его красоту. И чтобы иметь привилегированных пользователей, вам нужен модный экран входа в систему с различными ролями пользователей. И, как вы уже знаете, Google Web Toolkit — это правильный выбор для разработки вашего приложения … но нет поддержки для защиты (лучше сказать — скрытия) ваших диалогов, панелей, функций от любопытных, но непривилегированных пользователей. Так что не теряйте внимания и позвольте мне показать вам, как это сделать с помощью acris-security .
А что бы вы сказали об этом куске кода?
public class CustomerPanel extends SecuredComposite {
...
@Secured(Grants.SECURITY_MANAGEMENT)
protected TextBox securityID;
...
}
Ожидаете ли вы, что оно будет отображать довольно аккуратное текстовое поле только для тех, кто имеет право просматривать и управлять конфиденциальной информацией о клиенте? Да! Это сделает вещь. И это также будет отличать разрешение просто видеть (но не изменять) текстовое поле и фактически изменять значение. Но по одному шагу за раз …
Общий жизненный цикл «защищенной» сети
Давайте предположим следующий жизненный цикл нашего примера веб-приложения, который предоставляет пользователю панель входа в систему.
If the user is allowed to access the restricted area represented by a “secured” form, it will display it and applies authorities to the fields of the form. After the user fills the fields and saves the values he will log out. In case the user is not allowed, an informative page will pop-up and if the web is available for public access it can offer a registration form.
The aim of acris-security is to:
- Provide access to user services and offer couple of default implementations (currently Spring Security)
- Allow developers to implement client-side security using annotations and also have full programmatic control
- Be open to various extensions (own concepts of user relations to authorities, user services, ways of defining security constraints on components,…)
Client security
Enough of theory, let’s jump into an example. For the purpose of this article I will use the showcase available for acris-security. The showcase uses security annotations and also GWT’s UIBinder. The first and one of the most interesting panels is the CustomerPanel with fields ready to be secured:
public class CustomerPanel extends SecuredComposite {
interface CustomerViewUiBinder extends UiBinder<Widget, CustomerPanel> {}
private static CustomerViewUiBinder uiBinder = GWT.create(CustomerViewUiBinder.class);
@UiField
@Secured(value = Grants.SECURITY_MANAGEMENT, permission = Permission.CREATE)
protected TextBox name;
@UiField
@Secured(Grants.SECURITY_MANAGEMENT)
protected TextBox securityID;
public CustomerPanel() {
initWidget(uiBinder.createAndBindUi(this));
}
}
As you can see @UiField and @Secured (from the package sk.seges.acris.security.client.annotations) are not fighting against each other and live in perfect harmony. Also you can note a constant in Grants interface. A grant is a friendly representation of the authority describing what for it is. And an authority is a string token representing «what the user is allowed to do». Authorities are stored per user in a storage (database, file,…) or are “computed” e.g. from a user’s role (or using your own design of relations between users and authorities). A grant isn’t a sufficient description of what exact representation a component is. There must be an information about permission specifying what the user can actually do with the component secured by the grant. The authority wraps the grant and the permission into one string representation.
So if you annotate a component with Grants.SECURITY_MANAGEMENT (string “security_management”) you implicitly say that if the user has authority::
- ROLE_security_management_VIEW — he is allowed to just see but not change the value of the component
- ROLE_security_management_EDIT — he is allowed to put a value in there
And of course you can override this behaviour as in the case of “name” text box where the CREATE permission is required. More about the theory and vocabulary is available in the Concept section.
acris-security is in symbiosis with Spring Security and if you are already using Spring Security’s authorities you can reuse them on the client side directly. You just need to strip the “ROLE_” prefix and be conformant with the grant/permission stuff.
Fields are annotated, let’s instantiate the panel:
CustomerPanel customerPanel = GWT.create(CustomerPanel.class);
customerPanel.setClientSession(clientSession);
container.add(customerPanel);
Because acris-security (and also UiBinder in this case) uses deferred binding we have to use GWT.create call to correctly instantiate the panel. You might notice the middle line setting a client session. The client session (usually single for the application or part of the application’s context) actually holds the inforamtion about user and its session/conversation. There (apart of other possible values) is stored the user object with authorities and a session ID. After successful log-in the session is filled with the information and by injecting the session to the panel it will be able to compose correct representation based on available authorities.
Every panel willing to be secured must implement ISecuredObject interface. If you are extending GWT’s Composite it might be handy to use SecuredComposite and don’t bother with implementing required methods.
Log me in please…
Alright, we have secured the panel but now we need to log a user in somehow. To separate client and server side and to allow also different implementations there is IUserService interface available negotiating user’s credentials with an authentication and authorization service. This can be for example Spring Security authentication manager with a chain of providers (LDAP, CAS, database,…).
There is a default user service integrated with Spring Service called… UserService. Of course you can use your own/already existing implementation but it has to extend IUserService interface.
final IUserServiceAsync userService = GWT.create(IUserService.class);
SessionServiceDefTarget endpoint = (SessionServiceDefTarget) userService;
endpoint.setServiceEntryPoint("showcase-service/userService");
endpoint.setSession(clientSession);
User service is standard GWT RPC service. Calling login method and filling client session will prepare everything relevant for further securing the panel:
userService.login(new UserPasswordLoginToken(username.getText(), password.getText(), null),
new AsyncCallback<ClientSession>() {
@Override
public void onSuccess(ClientSession result) {
// copy the information about user and session
// so secured components can rely on it
clientSession.setSessionId(result.getSessionId());
clientSession.setUser(result.getUser());
loggedInCmd.execute();
}
@Override
public void onFailure(Throwable caught) {
GWT.log("Login failure", caught);
}
});
The actual placing of the panel is deferred to the loggedInCmd.execute(); call where a new panel is instantiated and put to the parent container.
Conclusion
This was a simple example showing how you can quickly enable your application with security using AcrIS. Its focus is on the integration of client and server and to easily control security on the client sidein a variety of ways. What it does not do is to focus on the server side security as such because that is solved by frameworks of your choice (like Spring Security does). It only bridges your server side choice with the GWT client.
We just scratched the surface about what acris-security is capable of and next time we will take a look at runtime security, how to reapply security constraints after re-logging as different user and maybe more.