Реагировать: реализовать вход с BASIC-аутентификацией

Я хочу создать реагирующее веб-приложение, содержащее логин. Мой бэкэнд реализован с аутентификацией JAX-RS и BASIC. Вот фрагмент web.xml и Resource.

<login-config>
    <auth-method>BASIC</auth-method>
</login-config>

<security-constraint>
    <web-resource-collection>
        <web-resource-name>api</web-resource-name>
        <url-pattern>/api/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <role-name>ROLE</role-name>
    </auth-constraint>
</security-constraint>

<security-role>
    <role-name>ROLE</role-name>
</security-role>

Ресурс:

@GET
@PermitAll
@Produces(MediaType.APPLICATION_JSON)
public List<OnlineStackDto> getData() {
    return dataService.getData();
}

Все запросы от реакции объединены в один файл request.js. Вот функция get в качестве примера.

const user = 'username';
const pass = 'password';

export function get(url) {
    return fetch(url, 
        {
            method: 'GET',
            headers: {
                Authorization: "Basic " + btoa(user + ":" + pass)
            }
        }
    );
}

Конечно, жесткое кодирование имени пользователя и пароля — это не то, как приложение можно развернуть позже. Функция вызывается так:

import {get} from "./request";
import React from "react";

export default class DataComponent extends React.Component {
    componentDidMount() {
        get('onlinestacks').then(data => this.setState({myData: data}));
    }
}

Логика входа реализована с использованием react-router и React 16.3 Context-API. Таким образом, я могу получить доступ к имени пользователя/паролю в каждом компоненте, используя Consumer. Вот пример того, как данные устанавливаются и используются. Более полный пример см. в этом вопросе.

export default class App extends Component {

    constructor(props) {
        super(props);
        this.state = {
            user: {
                username: '',
                password: ''
            }
        };

        this.login = this.login.bind(this);
    }

    login(username, password) {
        this.setState({
            user: {
                username: username,
                password: password
            }
        });
    }

    render() {
        return (
            <UserContext.Provider value={this.state.user}>
                <Header/>
                /*Other Components*/
            </UserContext.Provider>
        );
    }

}

export default class Header extends React.Component {

    render() {
        return (<header>
            <UserContext.Consumer>{value =>
                <div className="username">{value.username}</div>
            }
            </UserContext.Consumer>
        </header>);
    }
}

Мой вопрос:
Как я могу использовать имя пользователя и пароль из Context в своих запросах, без передачи их каждый раз, когда я хочу сделать запрос?


person SilverNak    schedule 14.04.2018    source источник
comment
Я не могу понять, какую цель вы хотите достичь. Вы хотите отправить какие-то запросы с именем пользователя, а какие-то без?   -  person degr    schedule 14.04.2018
comment
@degr Я хочу отправлять все запросы с именем пользователя. Но имя пользователя не должно быть жестко запрограммировано. Вместо этого я хочу, чтобы пользователь вводил свое имя и использовал его во всех запросах.   -  person SilverNak    schedule 14.04.2018
comment
Вам нужна помощь с назначением переменных? Вы не знаете, как получить значение из текстового ввода и установить его в переменную?   -  person degr    schedule 14.04.2018
comment
@degr У меня есть необходимая информация (имя пользователя, пароль) внутри файла UserContext. Я могу получить к нему доступ, как показано в компоненте Header. Я не знаю, как использовать эти данные в файле request.js. Просто установить его как глобальную переменную кажется неправильным, и единственный другой способ, который я мог придумать, - это передать пользователя в качестве параметра запроса, например function get(url, user). Но это потребовало бы реализации UserContext.Consumer в каждом компоненте, который должен получать данные. Это кажется излишне сложным.   -  person SilverNak    schedule 15.04.2018
comment
Можете ли вы привести пример, где вы вызываете метод request? Предлагаемое решение будет зависеть от этого. Есть много способов сделать это, и у каждого есть свои недостатки.   -  person Moti Korets    schedule 15.04.2018
comment
@MotiKorets В вопросе есть DataComponent, дающий пример запроса get   -  person SilverNak    schedule 16.04.2018


Ответы (1)


Есть много возможных решений для этого. Вы можете использовать метод bind и передать связанный метод get с установленными именем пользователя и паролем. Таким образом, вы получаете функцию, которая будет принимать username и password в качестве параметров.

export function get(username, password, url)

И вы Context.Provider передадите связанную функцию

<UserContext.Provider value={get.bind(null,this.state.user,this.state.password)}>

Чтобы не вызывать рефлоу, вы можете снять привязку

login(username, password){
  this.setState({
        authenticatedGet: get.bind(null,username,password)
  });
}
//....
<UserContext.Provider value={this.state.authenticatedGet}>

Я действительно не думаю, что вы должны использовать Context для такого рода вещей. Дело в том, что Context хорош для логики пользовательского интерфейса, и это больше касается сервисного уровня. Есть много других решений, включая передачу get в качестве реквизита, где это уместно, или, если вы используете redux с redux-thunk, передайте его как thunk.withExtraArgument, но это действительно выходит за рамки вашего вопроса.

person Moti Korets    schedule 16.04.2018
comment
Спасибо за Ваш ответ! Подумав, я пришел к выводу, что вы правы, и Context не следует использовать для этого. Теперь я реализую решение с помощью файлов cookie. - person SilverNak; 18.04.2018