- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在开发一个涉及 Symfony2 和 AngularJs 的 Web 应用程序。我对在站点中验证用户的正确方法有疑问。
我在我的 API REST(内置于 Symfony)中构建了一个函数,该函数通过请求中传递的参数对用户进行身份验证。
/**
* Hace el login de un usuario
*
* @Rest\View()
* @Rest\Post("/user/login")
* @RequestParam(name="mail", nullable=false, description="user email")
* @RequestParam(name="password", nullable=false, description="user password")
*/
public function userLoginAction(Request $request, ParamFetcher $paramFetcher) {
$mail = $paramFetcher->get('mail');
$password = $paramFetcher->get("password");
$response = [];
$userManager = $this->get('fos_user.user_manager');
$factory = $this->get('security.encoder_factory');
$user = $userManager->findUserByUsernameOrEmail($mail);
if (!$user) {
$response = [
'error' => 1,
'data' => 'No existe ese usuario'
];
} else {
$encoder = $factory->getEncoder($user);
$ok = ($encoder->isPasswordValid($user->getPassword(),$password,$user->getSalt()));
if ($ok) {
$token = new UsernamePasswordToken($user, null, "main", $user->getRoles());
$this->get("security.context")->setToken($token);
$event = new InteractiveLoginEvent($request, $token);
$this->get("event_dispatcher")->dispatch("security.interactive_login", $event);
if ($user->getType() == 'O4FUser') {
$url = $this->generateUrl('user_homepage');
} else {
$url = $this->generateUrl('gym_user_homepage');
}
$response = [
'url' => $url
];
} else {
$response = [
'error' => 1,
'data' => 'La contraseña no es correcta'
];
}
}
return $response;
}
最佳答案
正如我最近使用 Symfony2 和 Angular 完成的身份验证实现一样,经过大量研究后,我最终选择了最佳方式 API-Platform (使用 JSON-LD/Hydra 新词汇来提供 REST-API,而不是我想你使用的 FOSRest)和来自 Angular 前端应用程序的 restangular。
关于无状态,它确实是一个更好的解决方案,但你必须建立你的登录场景来选择最好的技术。
登录系统和 JWT 不是不兼容的,两种解决方案都可以使用。在使用 JWT 之前,我对 OAuth 进行了大量研究,显然实现起来很痛苦,并且需要一个完整的开发团队。 JWT 提供了最好和最简单的方法来实现这一点。
您应该首先考虑使用 FOSUser正如@chalasr 建议的那样捆绑。
此外,使用 API-Platform和 JWT Bundle from Lexik你需要NelmioCors对于应该出现的跨域错误:
(仔细阅读此捆绑包的文档)
HTTPS 协议(protocol)是 api 和 front 之间通信的强制性协议(protocol)!
在下面的示例代码中,我使用了特定的实体映射。
联系实体得到了抽象的通信方式,它得到了电话。稍后我将放置完整的映射和类示例)。
根据您的需要进行调整。
# composer.json
// ...
"require": {
// ...
"friendsofsymfony/user-bundle": "~2.0@dev",
"lexik/jwt-authentication-bundle": "^1.4",
"nelmio/cors-bundle": "~1.4",
"dunglas/api-bundle": "~1.1@beta"
// ...
# app/AppKernel.php
public function registerBundles()
{
$bundles = array(
// ...
new Symfony\Bundle\SecurityBundle\SecurityBundle(),
new FOS\UserBundle\FOSUserBundle(),
new Lexik\Bundle\JWTAuthenticationBundle\LexikJWTAuthenticationBundle(),
new Nelmio\CorsBundle\NelmioCorsBundle(),
new Dunglas\ApiBundle\DunglasApiBundle(),
// ...
);
# app/config/config.yml
imports:
// ...
- { resource: security.yml }
// ...
framework:
// ...
csrf_protection: ~
form: ~
session:
handler_id: ~
// ...
fos_user:
db_driver: orm
firewall_name: main
user_class: AppBundle\Entity\User
lexik_jwt_authentication:
private_key_path: %jwt_private_key_path%
public_key_path: %jwt_public_key_path%
pass_phrase: %jwt_key_pass_phrase%
token_ttl: %jwt_token_ttl%
// ...
dunglas_api:
title: "%api_name%"
description: "%api_description%"
enable_fos_user: true
nelmio_cors:
defaults:
allow_origin: ["%cors_allow_origin%"]
allow_methods: ["POST", "PUT", "GET", "DELETE", "OPTIONS"]
allow_headers: ["content-type", "authorization"]
expose_headers: ["link"]
max_age: 3600
paths:
'^/': ~
// ...
parameters:
database_host: 127.0.0.1
database_port: ~
database_name: symfony
database_user: root
database_password: ~
# You should uncomment this if you want use pdo_sqlite
# database_path: "%kernel.root_dir%/data.db3"
mailer_transport: smtp
mailer_host: 127.0.0.1
mailer_user: ~
mailer_password: ~
jwt_private_key_path: %kernel.root_dir%/var/jwt/private.pem
jwt_public_key_path: %kernel.root_dir%/var/jwt/public.pem
jwt_key_pass_phrase : 'test'
jwt_token_ttl: 86400
cors_allow_origin: http://localhost:9000
api_name: Your API name
api_description: The full description of your API
# A secret key that's used to generate certain security-related tokens
secret: ThisTokenIsNotSecretSoChangeIt
# src/AppBundle/Entity/User.php
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\User as BaseUser;
class User extends BaseUser
{
protected $id;
protected $username;
protected $email;
protected $plainPassword;
protected $enabled;
protected $roles;
}
# src/AppBundle/Resources/config/doctrine/User.orm.yml
AppBundle\Entity\User:
type: entity
table: fos_user
id:
id:
type: integer
generator:
strategy: AUTO
# app/config/security.yml
security:
encoders:
FOS\UserBundle\Model\UserInterface: bcrypt
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_ADMIN
providers:
fos_userbundle:
id: fos_user.user_provider.username
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
api:
pattern: ^/api
stateless: true
lexik_jwt:
authorization_header:
enabled: true
prefix: Bearer
query_parameter:
enabled: true
name: bearer
throw_exceptions: false
create_entry_point: true
main:
pattern: ^/
provider: fos_userbundle
stateless: true
form_login:
check_path: /login_check
username_parameter: username
password_parameter: password
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
require_previous_session: false
logout: true
anonymous: true
access_control:
- { path: ^/api, role: IS_AUTHENTICATED_FULLY }
# app/config/services.yml
services:
// ...
fos_user.doctrine_registry:
alias: doctrine
# app/config/routing.yml
api:
resource: "."
type: "api"
prefix: "/api"
api_login_check:
path: "/login_check"
$ npm install -g yo generator-angular bower
$ npm install -g ruby sass compass less
$ npm install -g grunt-cli karma-cli jshint node-gyp registry-url
$ yo angular
$ npm install karma jasmine-core grunt-karma karma-jasmine --save-dev
$ npm install phantomjs phantomjs-prebuilt karma-phantomjs-launcher --save-dev
$ bower install --save lodash#3.10.1
$ bower install --save restangular
# app/index.html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" href="styles/main.css">
</head>
<body ng-app="angularApp">
<div class="container">
<div ng-include="'views/main.html'" ng-controller="MainCtrl"></div>
<div ui-view></div>
<script src="bower_components/jquery/dist/jquery.js"></script>
<script src="bower_components/angular/angular.js"></script>
<script src="bower_components/bootstrap-sass-official/assets/javascripts/bootstrap.js"></script>
<script src="bower_components/restangular/dist/restangular.js"></script>
<script src="bower_components/lodash/lodash.js"></script>
<script src="scripts/app.js"></script>
<script src="scripts/controllers/main.js"></script>
</body>
</html>
# app/scripts/app.js
'use strict';
angular
.module('angularApp', ['restangular'])
.config(['RestangularProvider', function (RestangularProvider) {
// URL ENDPOINT TO SET HERE !!!
RestangularProvider.setBaseUrl('http://your_vhost/api');
RestangularProvider.setRestangularFields({
id: '@id'
});
RestangularProvider.setSelfLinkAbsoluteUrl(false);
RestangularProvider.addResponseInterceptor(function (data, operation) {
function populateHref(data) {
if (data['@id']) {
data.href = data['@id'].substring(1);
}
}
populateHref(data);
if ('getList' === operation) {
var collectionResponse = data['hydra:member'];
collectionResponse.metadata = {};
angular.forEach(data, function (value, key) {
if ('hydra:member' !== key) {
collectionResponse.metadata[key] = value;
}
});
angular.forEach(collectionResponse, function (value) {
populateHref(value);
});
return collectionResponse;
}
return data;
});
}])
;
# app/scripts/controllers/main.js
'use strict';
angular
.module('angularApp')
.controller('MainCtrl', function ($scope, $http, $window, Restangular) {
// fosuser user
$scope.user = {username: 'johndoe', password: 'test'};
// var to display login success or related error
$scope.message = '';
// In my example, we got contacts and phones
var contactApi = Restangular.all('contacts');
var phoneApi = Restangular.all('telephones');
// This function is launched when page is loaded or after login
function loadContacts() {
// get Contacts
contactApi.getList().then(function (contacts) {
$scope.contacts = contacts;
});
// get Phones (throught abstrat CommunicationWays alias moyensComm)
phoneApi.getList().then(function (phone) {
$scope.phone = phone;
});
// some vars set to default values
$scope.newContact = {};
$scope.newPhone = {};
$scope.contactSuccess = false;
$scope.phoneSuccess = false;
$scope.contactErrorTitle = false;
$scope.contactErrorDescription = false;
$scope.phoneErrorTitle = false;
$scope.phoneErrorDescription = false;
// contactForm handling
$scope.createContact = function (form) {
contactApi.post($scope.newContact).then(function () {
// load contacts & phones when a contact is added
loadContacts();
// show success message
$scope.contactSuccess = true;
$scope.contactErrorTitle = false;
$scope.contactErrorDescription = false;
// re-init contact form
$scope.newContact = {};
form.$setPristine();
// manage error handling
}, function (response) {
$scope.contactSuccess = false;
$scope.contactErrorTitle = response.data['hydra:title'];
$scope.contactErrorDescription = response.data['hydra:description'];
});
};
// Exactly same thing as above, but for phones
$scope.createPhone = function (form) {
phoneApi.post($scope.newPhone).then(function () {
loadContacts();
$scope.phoneSuccess = true;
$scope.phoneErrorTitle = false;
$scope.phoneErrorDescription = false;
$scope.newPhone = {};
form.$setPristine();
}, function (response) {
$scope.phoneSuccess = false;
$scope.phoneErrorTitle = response.data['hydra:title'];
$scope.phoneErrorDescription = response.data['hydra:description'];
});
};
}
// if a token exists in sessionStorage, we are authenticated !
if ($window.sessionStorage.token) {
$scope.isAuthenticated = true;
loadContacts();
}
// login form management
$scope.submit = function() {
// login check url to get token
$http({
method: 'POST',
url: 'http://your_vhost/login_check',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: $.param($scope.user)
// with success, we store token to sessionStorage
}).success(function(data) {
$window.sessionStorage.token = data.token;
$scope.message = 'Successful Authentication!';
$scope.isAuthenticated = true;
// ... and we load data
loadContacts();
// with error(s), we update message
}).error(function() {
$scope.message = 'Error: Invalid credentials';
delete $window.sessionStorage.token;
$scope.isAuthenticated = false;
});
};
// logout management
$scope.logout = function () {
$scope.message = '';
$scope.isAuthenticated = false;
delete $window.sessionStorage.token;
};
// This factory intercepts every request and put token on headers
}).factory('authInterceptor', function($rootScope, $q, $window) {
return {
request: function (config) {
config.headers = config.headers || {};
if ($window.sessionStorage.token) {
config.headers.Authorization = 'Bearer ' + $window.sessionStorage.token;
}
return config;
},
response: function (response) {
if (response.status === 401) {
// if 401 unauthenticated
}
return response || $q.when(response);
}
};
// call the factory ...
}).config(function ($httpProvider) {
$httpProvider.interceptors.push('authInterceptor');
});
<!—Displays error or success messages-->
<span>{{message}}</span><br><br>
<!—Login/logout form-->
<form ng-show="!isAuthenticated" ng-submit="submit()">
<label>Login Form:</label><br>
<input ng-model="user.username" type="text" name="user" placeholder="Username" disabled="true" />
<input ng-model="user.password" type="password" name="pass" placeholder="Password" disabled="true" />
<input type="submit" value="Login" />
</form>
<div ng-show="isAuthenticated">
<a ng-click="logout()" href="">Logout</a>
</div>
<div ui-view ng-show="isAuthenticated"></div>
<br><br>
<!—Displays contacts list-->
<h1 ng-show="isAuthenticated">Liste des Contacts</h1>
<article ng-repeat="contact in contacts" ng-show="isAuthenticated" id="{{ contact['@id'] }}" class="row marketing">
<h2>{{ contact.nom }}</h2>
<!—Displays contact phones list-->
<h3 ng-repeat="moyenComm in contact.moyensComm">Tél : {{ moyenComm.numero }}</h3>
</article><hr>
<!—Create contact form-->
<form name="createContactForm" ng-submit="createContact(createContactForm)" ng-show="isAuthenticated" class="row marketing">
<h2>Création d'un nouveau contact</h2>
<!—Displays error / success message on creating contact-->
<div ng-show="contactSuccess" class="alert alert-success" role="alert">Contact publié.</div>
<div ng-show="contactErrorTitle" class="alert alert-danger" role="alert">
<b>{{ contactErrorTitle }}</b><br>
{{ contactErrorDescription }}
</div>
<div class="form-group">
<input ng-model="newContact.nom" placeholder="Nom" class="form-control">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<!—Phone form-->
<form name="createPhoneForm" ng-submit="createPhone(createPhoneForm)" ng-show="isAuthenticated" class="row marketing">
<h2>Création d'un nouveau téléphone</h2>
<div ng-show="phoneSuccess" class="alert alert-success" role="alert">Téléphone publié.</div>
<div ng-show="phoneErrorTitle" class="alert alert-danger" role="alert">
<b>{{ phoneErrorTitle }}</b><br>
{{ phoneErrorDescription }}
</div>
<div class="form-group">
<input ng-model="newPhone.numero" placeholder="Numéro" class="form-control">
</div>
<div class="form-group">
<label for="contact">Contact</label>
<!—SelectBox de liste de contacts-->
<select ng-model="newPhone.contact" ng-options="contact['@id'] as contact.nom for contact in contacts" id="contact"></select>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
关于angularjs - Symfony2 和 Angular。用户认证,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35344563/
我开始从事一个用 Symfony 2.8 编写的大型项目。将整个项目升级到 SF 3 需要数百小时,现在还不可能。我想到了一个想法,将 symfony/symfony 包解压到它替换的单个包中(com
我在提交表单后使用 FOSUserEvents,但订阅者调用了两次。 这样我的验证码第一次有效第二次无效 这是我的代码 router = $router; $this->request
我有以下路线: blog_show: path: /test/123 defaults: { _controller: TotalcanBravofillBundle:Te
我是测试新手。我想测试我的功能。我已经成功安装了 phpUnit。我在互联网上查看了许多教程。但我无法获得有关测试的正确信息。这是我的功能代码: public function loginAction
我正在尝试重现 facebook batch requests 的行为在他们的图形 api 上运行。 所以我认为最简单的解决方案是在 Controller 上向我的应用程序发出几个请求,例如: pub
在 Symfony Progress Bar documentation有一个超酷酒吧的示例图像。不幸的是,看起来文档的其余部分没有解释如何获得这样的结果。 这是图片,以防您错过: 我怎么才能得到它?
我使用Finder发送假脱机电子邮件,但是自动名称生成器将点放在文件名中,有时它们出现在文件的开头。 查找程序似乎无法获取具有该名称的文件-那些文件被隐藏了……有人经历过这种行为吗?有什么建议如何使用
我正在尝试进行 LDAP 身份验证,我目前遇到此类错误: ServiceNotFoundException: The service "security.firewall.map.context.ma
有没有办法验证和检查集合数组是否为空。我已经尝试过: /** * @Assert\NotBlank() * @Assert\Length( min = 1) */ protected $work
使用Smyfony2和Doctrin2,可以使用以下示例创建数据固定装置:http://symfony.com/doc/current/bundles/DoctrineFixturesBundle/i
我看到在大多数Symfony 2示例中,例如,不存在记录时,Symfony 2会引发异常。我认为这种方法对最终用户不友好。为什么有人更喜欢引发异常而不在Flashbag上添加一些错误消息? 最佳答案
我对项目中的以下服务有疑问: app.security.guardAuthenticatorLoginPassword: class: AppBundle\Security\LoginPa
symfony缓存和登录Docker容器存在问题。 Web服务器从www-data用户和组执行,当我使用docker上安装的php从docker容器中清除symfony缓存时,它从root执行。 因此
我想了解 symfony 中的服务 我已阅读http://symfony.com/doc/2.3/book/service_container.html#creating-configuring-se
因为我对 Symfony 和 Doctrine 还很陌生,所以我有一个可能很愚蠢的问题;-) 有人可以用简单的词语向我解释集合(尤其是实体中的ArrayCollections)吗?它是什么以及何时以及
我收到了这个表格: {{ form_start(form) }} {{ form_end(form) }} 我想检查用户是否登录,我这样做了: {% if is_g
我的网站已准备好部署,我正在尝试将其设置为在线。 一些信息: 主持人是 OVH。 它不允许 SSH,我必须使用 FTP 发送文件。也没有命令行。 我现在希望能够在子目录中设置网站:/www/test(
过去几个月以来,我一直在尝试与symfony合作。昨晚我自动删除了不需要的存储库。之后,我无法使用symfony命令创建新的symfony项目。当我在终端中运行Symfony new Security
In the environnement variable, then in system variable, I edited the path and added在环境变量中,然后在系统变量
我们有一个 Symfony 1.4 应用程序,想升级到 Symfony 4。是否有可能或者我们必须重新编程该应用程序? 我们询问了我们附近的一家软件公司,他们告诉我们必须重新编写应用程序。 最佳答案
我是一名优秀的程序员,十分优秀!