Персонализиран модул за влизане в Wildfly 8, който има достъп до параметрите на HttpServletRequest

Проучвам осъществимостта на използването на персонализиран модул за влизане на wildfly.

Клиентът ще предаде идентификатора на мобилното устройство на сървъра като част от влизането. Ще проверя дали потребителското име и паролата са правилни по обичайния начин, след което трябва да проверя дали мобилното устройство е одобрено за използване на услугата.

Идеята е, че ще имам спокоен метод за влизане в уеб услуга, който извиква HttpServletRequest.login(u, p).

Как да получа идентификатора на мобилното устройство в модула за влизане в HttpServletRequest?

Мога да вляза и ако това успее, тогава да тествам идентификатора на устройството в уеб услугата и ако това не е одобрено, да изляза потребителя. Но това изглежда доста объркано.

Какъв е правилният начин да направите това?

РЕДАКТИРАНЕ

ОБРАТНА ИНФОРМАЦИЯ: Направих го по начина, по който Крис предложи. Приложих собствена версия на CallBackHandler и реализация на интерфейса за обратно извикване, вътре в метода за влизане на моя модул за влизане правя следното:

public boolean login() throws LoginException {

        boolean login = super.login();

        if (login) {

            UuidCallback uuidCallback = new UuidCallback();

            try {

                super.callbackHandler.handle(new Callback[]{uuidCallback});

            } catch (Exception e) {

                LoginException le = new LoginException("Failed to get uuid");
                le.initCause(e);

                throw le;
            }

            System.out.print("Device UUID: "+uuidCallback.getUuid());
    }

    return login;
}

В метода за влизане в уеб услугата:

@Path("/login")
@Produces({ "application/json" })
public class LoginWebService {

    @POST
    public Response login(@Context HttpServletRequest request) throws LoginException {

        CallbackHandler callbackHandler = new MyCallbackHandler(request.getParameter("username"), request.getParameter("password"), request.getParameter("uuid"));

        Subject subject = new Subject();
        LoginContext loginContext = new LoginContext("securityDomain", new subject, callbackHandler);

        loginContext.login();

        MyPrincipal principal = subject.getPrincipals(MyPrincipal.class).iterator().next();
    }
}

Можете също така просто да зададете uuid на манипулатора за обратно извикване и след това да извикате getUUID() на манипулатора за обратно извикване вътре в метода LoginModule.login. Но аз избрах да се придържам към дизайна, въпреки че няма смисъл за мен.

Все още получавах 403, когато влязох и се опитах да осъществя достъп до защитени ресурси, оказа се, че ако auth-constraint/role-name е *, трябва да предоставите поне едно security-role.

<login-config>
    <auth-method>FORM</auth-method>
    <realm-name>mydomain</realm-name>
</login-config>
<security-constraint>
    <web-resource-collection>
        <web-resource-name>rest</web-resource-name>
        <url-pattern>/rest/app/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <role-name>*</role-name>
    </auth-constraint>
</security-constraint>
<!-- after login there is a 403 on protected resources if no role and role-name * -->
<security-role>
    <role-name>user</role-name>
</security-role>

Всички мои потребители имат потребителска роля, която им дава достъп. Успях да го накарам да работи, като изключих security-role, но след това auth-constraint/role-name трябва да бъде зададена на буквална роля, в моя случай: "потребител"


person Rian    schedule 04.12.2015    source източник


Отговори (2)


Бих предложил да създадете LoginContext и да преминете през изпълнение на CallbackHandler. В callbackhandler се грижи за допълнителното свойство UUID.

Въпреки че този код работи за мен, ще трябва да го актуализирате, за да се погрижите за допълнителното UUID свойство

public class NamePasswordCallbackHandler implements CallbackHandler {
    private final String username;
    private final String password;

    private NamePasswordCallbackHandler(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
        for (Callback current : callbacks) {
            if (current instanceof NameCallback) {
                ((NameCallback) current).setName(username);
            } else if (current instanceof PasswordCallback) {
                ((PasswordCallback) current).setPassword(password.toCharArray());
            } else {
                throw new UnsupportedCallbackException(current);
            }
        }
    }
}

Създадох моя собствена реализация на обекта Configuration (показан по-долу като JBossJaasConfiguration).

След това прекарайте този манипулатор на обратно извикване към вашия LoginContext:

CallbackHandler cbh = new NamePasswordCallbackHandler(username, password);
Configuration config = new JBossJaasConfiguration("mysqldomain");

LoginContext loginContext = new LoginContext("mycontext", new Subject(), cbh, config);
loginContext.login();

Свойството "mysqldomain" се отнася до името на защитения домейн във вашия standalone.xml

<subsystem xmlns="urn:jboss:domain:security:1.2">
    <security-domains>
        <security-domain name="mysqldomain" cache-type="default">
             <authentication>
                  <login-module code="com.soccerx.security.DatabaseServerLoginRealm" flag="required">
                     <module-option name="dsJndiName" value="java:jboss/datasources/SoccerSoftwareDS"/>
                     <module-option name="principalsQuery" value="select userId, tenantId, password, salt from User where username=? and StatusId != 2"/>

            <module-option name="rolesQuery" value="select Role, 'Roles' from User where Username=?"/>
                    <module-option name="password-stacking" value="useFirstPass"/>
                    <module-option name="principalClass" value="com.soccerx.security.DatabasePrincipal"/>
                </login-module>
            </authentication>
        </security-domain>
    <security-domains>
</subsystem>

Въпреки че това не е пълно решение за вашите нужди, очаквам да можете да го промените, за да сте сигурни, че влизането е неуспешно, ако UUID не съвпада.

Забележка: Ще трябва да се погрижите за това във вашия CustomDatabaseLoginRealm, както е дефинирано във вашия standalone.xml. Това означава достъп до потребителското име/парола/uuid и сравняване със стойностите в базата данни.

Още повече документация вижте тук

person Chris Ritchie    schedule 04.12.2015
comment
За да потвърдя, ще създавам ли LoginContext и ще извиквам ли login от моя метод за влизане в уеб услугата? - person Rian; 05.12.2015
comment
Да точно. Също така създайте обект MobileIdCallback. - person Chris Ritchie; 07.12.2015

Вместо да извиквате метода HttpServletRequest.login() директно от вашата спокойна уеб услуга, можете да конфигурирате защитен домейн в Wildfly.

Този домейн за сигурност трябва да бъде упоменат в елемента за влизане в конфигурацията на файла web.xml на вашето уеб приложение с ограничение за сигурност, за да се активира JAAS

Ето пример за уеб. xml файл, който декларира ограничение за сигурност и конфигурация за влизане (с ОСНОВЕН метод за удостоверяване)

И примерна конфигурация на Wildfly standalone.xml за конфигурация на security-domain, като се използва стандартно осигурен модул за влизане в база данни, а не персонализиран:

<security-domain name="securityDomain" cache-type="default">
    <authentication>
        <login-module code="Database" flag="required">
            <module-option name="dsJndiName" value="java:/TestDS"/>
            <module-option name="principalsQuery" value="select password from User where login = ? and (disabled is null or disabled = 0) and activated = 1"/>
            <module-option name="rolesQuery" value="select name,'Roles' from Role r, User_Role ur, User u where u.login=? and u.id = ur.userId and r.id = ur.roleId"/>
            <module-option name="hashAlgorithm" value="SHA-256"/>
            <module-option name="hashEncoding" value="base64"/>
            <module-option name="unauthenticatedIdentity" value="guest"/>
        </login-module>
    </authentication>
</security-domain>

След това, във вашия REST ресурс, просто трябва да използвате анотациите @RolesAllowed или @PermitAll, за да ограничите достъпа или да не правите също оторизация.

person Rémi Bantos    schedule 04.12.2015