I use Laravel5.2 to build a E-commerce platform and get some troubles.When I loginout user state and I use Session::forget('user') or $request->session()->forget('user'), it works in current page,and I redirect to the login page and dd(Session::all()) find the session 'user' still exist!!! so I am confused to know what is wrong with it? plz tell me the reason,thanks all.
ps. loginout code
public function logout(Request $request)
{
if ($request->session()->has('user')) {
$is_forgotten = $request->session()->forget('user');
if ($is_forgotten === null)
echo json_encode(['result' => 1]);
exit;
}
echo json_encode(['result'=>0,'msg'=>'loginout error']);
exit;
}
The session doesn't get saved until the response is sent and events/middleware are triggered.
Try doing:
public function logout(Request $request)
{
if ($request->session()->has('user')) {
$is_forgotten = $request->session()->forget('user');
if ($is_forgotten === null)
return Response::json(['result' => 1]);
return '';
}
return Response::json(['result'=>0,'msg'=>'loginout error']);
}
Or if your using the helpers:
public function logout(Request $request)
{
if ($request->session()->has('user')) {
$is_forgotten = $request->session()->forget('user');
if ($is_forgotten === null)
return response()->json(['result' => 1]);
return '';
}
return response()->json(['result'=>0,'msg'=>'loginout error']);
}
Related
I have functions that I use in my Article model, they add likes to cookies for a specific article and record the time
public static function hasLikedToday($articleId, string $type)
{
$articleLikesJson = \Cookie::get('article_likes', '{}');
$articleLikes = json_decode($articleLikesJson, true);
// Check if there are any likes for this article
if (! array_key_exists($articleId, $articleLikes)) {
return false;
}
// Check if there are any likes with the given type
if (! array_key_exists($type, $articleLikes[$articleId])) {
return false;
}
$likeDatetime = Carbon::createFromFormat('Y-m-d H:i:s', $articleLikes[$articleId][$type]);
return ! $likeDatetime->addDay()->lt(now());
}
public static function setLikeCookie($articleId, string $type)
{
// Initialize the cookie default
$articleLikesJson = \Cookie::get('article_likes', '[]');
$articleLikes = json_decode($articleLikesJson, true);
// Update the selected articles type
$articleLikes[$articleId][$type] = today()->format('Y-m-d H:i:s');
$articleLikesJson = json_encode($articleLikes);
return cookie()->forever('article_likes', $articleLikesJson);
}
The php.blade page itself has buttons
Like Heart
Like Finger
Here are the routes web.php
Route::get('/article', function () {
$articleLikesJson = \Cookie::get('article_likes', '{}');
return view('article')->with([
'articleLikesJson' => $articleLikesJson,
]);
});
Route::get('article/{id}/like', 'App\Http\Controllers\ArticleController#postLike');
And the postLike() function itself in the controller
public function postLike($id) {
$article = Article::find($id);
$like = request('like');
if ($article->hasLikedToday($article->id, $like)) {
return response()
->json([
'message' => 'You have already liked the Article #'.$article->id.' with '.$like.'.',
]);
}
$cookie = $article->setLikeCookie($article->id, $like);
$article->increment('like_{$like}');
return response()
->json([
'message' => 'Liked the Article #'.$article->id.' with '.$like.'.',
'cookie_json' => $cookie->getValue(),
])
->withCookie($cookie);
}
In general, what is the problem, I have 2 types of likes that can be seen in php.blade, and the problem is to pass the choice of the type of like to the postLike() function, if in my function instead of $like I write 'heart', then everything will be work, but I need to determine which type we choose (heart or finger), tell me how this can be done?
You can use Laravel's Request object.
https://laravel.com/docs/8.x/requests#input
Like this:
use Illuminate\Http\Request;
public function postLike($id, Request $request)
{
$type = $request->input('type');
}
I'm trying to fix an if-else statement in the request for my controller. What I'm trying to do is: if the auth::user-companyID == $request-companyID then true else false; The companyID for the request is in a hidden field on the blade file.
CustomRequest
public function authorize()
{
$user = Auth::user();
if ($user->companyID == $request->companyID) {
return true;
} else {
return false;
}
}
Controller
public function edit(EquipmentRequest $request, $id)
{
$validated = $request->validated();
$user = Auth::user();
$equipment = EquipmentModel::where('id', '=', $id)->first();
$equipment->Year = $request->Year;
$equipment->Make = $request->Make;
$equipment->Model = $request->Model;
$equipment->Type = $request->Type;
$equipment->unitNumber = $request->unitNumber;
$equipment->AnnualInspectionDate = $request->AnnualInspectionDate;
$equipment->userID = $request->userID;
$equipment->companyID = $user->companyID;
$e = $equipment->save();
if ($e) {
$request->session()->flash('success', 'The equipment was successfully updated.');
} else {
$request->session()->flash('error',
'An error occurred while saving. Please refresh your browser and try again.');
}
return redirect()->route('equipmentlist');
}
This form worked before I started messing with it so I know the form is working correctly on the blade file. I'm not sure if you can pass the request data the way I'm doing it or if I have to do a construct to do it this way. I would really appreciate any advice.
use Illuminate\Http\Request;
public function authorize()
{
$user = auth()->user();
return $user->companyID === request()->companyID;
}
In my unit test, I have a user for whom I generate a token:
$tokenString = $this->user->createToken('PHPunit', ['example'])->accessToken;
How can I afterward delete this user's token?
This is what I do when a user logged out.
public function logout() {
Auth::user()->tokens->each(function($token, $key) {
$token->delete();
});
return response()->json('Successfully logged out');
}
This code will remove each token the user generated.
I think something like this can revoke the token:
$this->user->token()->revoke()
Based on this link.
Laravel Sanctum documentation stated 3 different ways to revoke tokens. you can find it here.
but for most cases we just revoke all user's tokens via:
// Revoke all tokens...
auth()->user()->tokens()->delete();
note: for some reason intelephense gives an error saying tokens() method not defined but the code works fine. Hirotaka Miyata found a workaround here.
so the over all logout method can be something like this:
public function logout()
{
//the comment below just to ignore intelephense(1013) annoying error.
/** #var \App\Models\User $user **/
$user = Auth::user();
$user->tokens()->delete();
return [
'message' => 'logged out'
];
}
the best working solution is this
public function logout(LogoutRequest $request): \Illuminate\Http\JsonResponse
{
if(!$user = User::where('uuid',$request->uuid)->first())
return $this->failResponse("User not found!", 401);
try {
$this->revokeTokens($user->tokens);
return $this->successResponse([
], HTTP_OK, 'Successfully Logout');
}catch (\Exception $exception) {
ExceptionLog::exception($exception);
return $this->failResponse($exception->getMessage());
}
}
public function revokeTokens($userTokens)
{
foreach($userTokens as $token) {
$token->revoke();
}
}
public function __invoke(Request $request)
{
$request->user()
->tokens
->each(function ($token, $key) {
$this->revokeAccessAndRefreshTokens($token->id);
});
return response()->json('Logged out successfully', 200);
}
protected function revokeAccessAndRefreshTokens($tokenId) {
$tokenRepository = app('Laravel\Passport\TokenRepository');
$refreshTokenRepository = app('Laravel\Passport\RefreshTokenRepository');
$tokenRepository->revokeAccessToken($tokenId);
$refreshTokenRepository->revokeRefreshTokensByAccessTokenId($tokenId);
}
How to print out data within function beforeAction? I want to make some verification before each action in a controller, therefore if some condition occurs in beforeAction I should print out data and prevent further execution, for example, JSON:
[
status: "error",
msg: "access denied"
]
I try to even inner redirect to another controller, but it doesn't work.
public function beforeAction($action)
{
$request = Yii::$app->request;
if ( ! checkByToken($request->get('token')) && $this->getRoute() != 'web/abonent/token_error') {
\Yii::$app->runAction('web/abonent/token_error');
return true;
}
return parent::beforeAction($action); // TODO: Change the autogenerated stub
}
But maybe there an another concept of doing so. I just need to check the condition before any actions and print our result or let the action execute.
To prevent further execution:
public function beforeAction($action) {
return false; // key point
}
To print out data within beforeAction:
public function beforeAction($action) {
// set response format = json:
Yii::$app->response->format = Response::FORMAT_JSON;
// then, set the response data:
Yii::$app->response->data = [
'status' => 'error',
'msg' => 'access denied'
];
return false;
}
I think will be better
public function beforeAction($action)
{
$request = Yii::$app->request;
if ( ! checkByToken($request->get('token')) && $this->getRoute() != 'web/abonent/token_error') {
$action = 'error';
}
return parent::beforeAction($action); // TODO: Change the autogenerated stub
}
Action name must be 'actionError'
When a user logged out from a perticular device I want to logout from all the device he has logged in till now . How I do it in Laravel.
I have used Redis for keeping the userId in Session by installing "predis/predis": "~1.0"
And Here is my controller for SignIn and Logout:
public function postSignIn(Request $request)
{
if (Auth::attempt(['email' => $request['email'], 'password' =>$request['password'] ]) ) {
$redis = \Redis::connection();
$userId=Session::getId();
$redis->sadd('users:sessions:'.$userId,Session::getId());
return redirect()->route('main');
}
return redirect()->back();
}
public function getLogout()
{
$redis = Redis::connection();
$userId=Session::getId();
$userSessions = $redis->smembers('user:sessions:' . $userId);
$currentSession = Session::getId();
foreach ($userSessions as $sessionId) {
if ($currentSession == $sessionId) {
continue;
}
$redis->srem('user:sessions:' . $userId, $sessionId);
$redis->del('laravel:' . $sessionId);
}
Auth::logout();
return redirect()->route('main');
}
It's successfully get logged in and also logged out but it doesn't kill all the session in other devices.
How do I solve the problem?
So issue was with typo in redis key name,
for write data used
$redis->sadd('users:sessions:'.$userId,Session::getId());
where key's prefix 'users:sessions:' and for get data used
$redis->srem('user:sessions:' . $userId, $sessionId); where key's prefix 'user:sessions:'
Thats why code didn't work and dd() returned empty array.
so correct code looks like this
public function postSignIn(Request $request)
{
if (Auth::attempt(['email' => $request['email'], 'password' =>$request['password'] ]) ) {
$redis = \Redis::connection();
$userId=Session::getId();
$redis->sadd('user:sessions:'.$userId,Session::getId());
return redirect()->route('main');
}
return redirect()->back();
}
public function getLogout()
{
$redis = Redis::connection();
$userId=Session::getId();
$userSessions = $redis->smembers('user:sessions:' . $userId);
$currentSession = Session::getId();
foreach ($userSessions as $sessionId) {
if ($currentSession == $sessionId) {
continue;
}
$redis->srem('user:sessions:' . $userId, $sessionId);
$redis->del('laravel:' . $sessionId);
}
Auth::logout();
return redirect()->route('main');
}
I have a suggestion/workaround for Your problem:
Working with sessions which are not stored in database is pain in a**, so You have to think differently in order to solve the problem. The different solution would be to record user id and time, when user logout. Then create middleware which will disconnects user if connection is older than last logout date. And thats all.
My prototype would look like so:
In postSignIn method line below will record user (session) login date: app('request')->session()->put('login_date', time());
In getLogout method line below will record user logout date globally:
\Cache::put('last_logout_'.\Auth::id(), time());
And final touch would be middleware with code similar to this:
if ($user = \Auth::user()) {
$login_date = app('request')->session()->get('login_date');
$last_logout_date = \Cache::get('last_logout_' . $user->id, time() + 100);
if ($login_date < $last_logout_date) {
\Auth::logout();
//redirect, error message...
}
}
Full code:
Methods:
public function postSignIn(Request $request)
{
if (Auth::attempt(['email' => $request['email'], 'password' => $request['password']])) {
app('request')->session()->put('login_date', time());
return redirect()->route('main');
}
return redirect()->back();
}
public function getLogout()
{
\Cache::put('last_logout_' . \Auth::id(), time());
Auth::logout();
return redirect()->route('main');
}
Middleware:
<?php
namespace App\Http\Middleware;
use Closure;
class LogoutIfExpired
{
public function handle($request, Closure $next, $guard = null)
{
if ($user = \Auth::user()) {
$login_date = app('request')->session()->get('login_date');
$last_logout_date = \Cache::get('last_logout_' . $user->id, time() + 100);
if ($login_date < $last_logout_date) {
\Auth::logout();
return redirect()->route('main');
}
}
return $next($request);
}
}