Flutter会话管理插件session_shelf的使用

Flutter会话管理插件session_shelf的使用

session_shelfshelf 框架的一个实现,用于通过文件或SQL数据库存储会话信息。该插件基于 mezonishelf_session,具有更强大的功能。

关于

session_shelf 添加了两个中间件给 shelf

  • cookiesMiddleware
  • sessionMiddleware

cookiesMiddleware 可以独立使用。 sessionMiddleware 依赖于 cookiesMiddleware 并必须在其之后使用。

final pipeline = const Pipeline()
    .addMiddleware(cookiesMiddleware())
    .addMiddleware(sessionMiddleware())
    .addHandler(handler);

示例代码

以下是一个简单的可运行示例:

import 'dart:io' show Cookie;

import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as io;
import 'package:shelf_router/shelf_router.dart';
import 'package:session_shelf/session_shelf.dart';
import 'package:shelf_static/shelf_static.dart';

void main(List<String> args) async {
  final router = Router();
  router.get('/', _handleHome);
  router.get('/login', _handleLogin);
  router.get('/login/', _handleLogin);
  router.post('/login', _handleLogin);
  router.post('/login/', _handleLogin);
  router.get('/logout', _handleLogout);
  router.get('/logout/', _handleLogout);
  final staticHandler = createStaticHandler('web', defaultDocument: 'index.html');
  final handler = Cascade().add(staticHandler).add(router).handler;
  final pipeline = const Pipeline()
      .addMiddleware(logRequests())
      .addMiddleware(cookiesMiddleware())
      .addMiddleware(sessionMiddleware())
      .addHandler(handler);
  const address = 'localhost';
  const port = 8080;
  final server = await io.serve(pipeline, address, port);
  print('Serving at http://${server.address.host}:${server.port}');
}

const _menu = '''
<a href="/">Home</a><br />
<a href="/login">Log in</a><br />
<a href="/logout">Log out</a><br />''';

Future<Response> _handleHome(Request request) async {
  final userManager = UserManager();
  final user = await userManager.getUser(request);
  var body = '$_menu{{message}}<br />{{cookies}}';
  if (user == null) {
    body = body.replaceAll('{{message}}', 'You are not logged in');
  } else {
    body = body.replaceAll('{{message}}', 'You are logged in as ${user.name}');
  }

  final cookies = request.getCookies();
  body = body.replaceAll('{{cookies}}',
      cookies.entries.map((e) => '${e.key}: ${e.value}').join('<br />'));

  request.addCookie(Cookie('foo', 'Foo'));
  if (!cookies.containsKey('baz')) {
    request.addCookie(Cookie('baz', 'Baz'));
  } else {
    request.removeCookie(Cookie('baz', ''));
  }

  return _render(body);
}

Future<Response> _handleLogin(Request request) async {
  const html = '''
<form action="" method="post">
<label>Login</label><br />
<input name="login" type="text" /><br />
<label>Password</label><br />
<input name="password" type="password" /><br /><br />
<button>Log in</button>
</form>
''';

  if (request.method == 'GET') {
    return _render(_menu + html);
  }

  final body = await request.readAsString();
  final queryParameters = Uri(query: body).queryParameters;
  final login = queryParameters['login'] ?? ''
    ..trim();
  final password = queryParameters['password'] ?? ''
    ..trim();
  if (login.isEmpty || password.isEmpty) {
    return _render(_menu + html);
  }

  final user = User(login);
  final userManager = UserManager();
  await userManager.setUser(request, user);
  return Response.found('/');
}

Future<Response> _handleLogout(Request request) async {
  await Session.deleteSession(request);
  return Response.found('/');
}

Response _render(String body) {
  return Response.ok(body, headers: {
    'Content-type': 'text/html; charset=UTF-8',
  });
}

class User {
  final String name;

  User(this.name);
}

class UserManager {
  Future<User?> getUser(Request request) async {
    final session = await Session.getSession(request);
    if (session == null) {
      return null;
    }

    final user = session.data['user'];
    if (user is User) {
      return user;
    }

    return null;
  }

  Future<User> setUser(Request request, User user) async {
    var session = await Session.getSession(request);
    session ??= await Session.createSession(request);
    session.data['user'] = user;
    return user;
  }
}

存储会话数据

默认情况下,会话数据存储在运行时的哈希映射中。这表示会话数据只应为授权用户创建。这种方法可以防止各种类型的攻击导致内存溢出。会话数据在过期后会自动删除(这会导致内存被释放)。定时器不用于这些目的,而是发生在新会话创建时。

文件存储

要将会话数据存储在文件中,请使用 FileStorage.plain

main() {
  Session.storage = FileStorage.plain(Directory('session_shelf'));
}

加密文件存储

要将加密的会话数据存储在文件中,请使用 FileStorage.crypto

main() {
  Session.storage = FileStorage.crypto(Directory('session_shelf'), algorithm, secretKey);
}

SQL数据库存储

要将会话数据存储在SQL数据库中,请使用 SqlStorage

final db = sqlite3.openInMemory();

main() async {
  Session.storage = SqlStorage('session_shelf', db.execute, (sql) {
    final ResultSet resultSet = db.select(sql);
    return resultSet;
  });
  await Session.storage.createTable();
}

加密SQL数据库存储

要将加密的会话数据存储在SQL数据库中,请使用 SqlCryptoStorage

final db = sqlite3.openInMemory();

main() async {
  Session.storage = SqlCryptoStorage('session_shelf_crypto', db.execute, (sql) {
    final ResultSet resultSet = db.select(sql);
    return resultSet;
    // 这只是一个示例。请勿在代码中写入您的密钥。
  }, algorithm, secretKey);
  await Session.storage.createTable();
}

处理复杂类型的数据

如果您的数据包含非基本类型,可以设置自定义序列化方法:

void setupJsonSerializer() {
  Session.toEncodable = (obj) {
    if (obj is User) {
      return {
        'type': 'User',
        'name': obj.name,
      };
    }
    return obj;
  };
  Session.reviver = (k, v) {
    if (v is Map && v.length == 2 && v['type'] == 'User' && v.containsKey('name')) {
      return User(v['name'] as String);
    }
    return v;
  };
}

更多关于Flutter会话管理插件session_shelf的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter会话管理插件session_shelf的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


session_shelf 是一个用于 Flutter 的会话管理插件,它基于 shelf 包,用于在 Flutter 应用中管理会话。shelf 是一个用于构建 Dart 服务器的中间件,而 session_shelf 则是用于处理会话管理的中间件。

以下是使用 session_shelf 插件的基本步骤:

1. 添加依赖

首先,你需要在 pubspec.yaml 文件中添加 session_shelf 依赖:

dependencies:
  flutter:
    sdk: flutter
  shelf: ^1.1.0
  session_shelf: ^0.0.1

然后运行 flutter pub get 来安装依赖。

2. 导入库

在你的 Dart 文件中导入所需的库:

import 'package:shelf/shelf.dart' as shelf;
import 'package:shelf/shelf_io.dart' as io;
import 'package:session_shelf/session_shelf.dart';

3. 创建会话管理器

你可以使用 SessionManager 来管理会话。以下是一个简单的例子:

void main() async {
  // 创建会话管理器
  var sessionManager = SessionManager();

  // 创建一个简单的处理程序
  var handler = const shelf.Pipeline()
      .addMiddleware(sessionManager.middleware)
      .addHandler(_handleRequest);

  // 启动服务器
  var server = await io.serve(handler, 'localhost', 8080);
  print('Server listening on port ${server.port}');
}

// 处理请求的函数
shelf.Response _handleRequest(shelf.Request request) {
  // 从请求中获取会话
  var session = request.context['session'] as Session;

  // 检查会话中是否有某个键值
  if (session['username'] == null) {
    session['username'] = 'Guest';
  }

  // 返回响应
  return shelf.Response.ok('Hello, ${session['username']}!');
}

4. 运行服务器

运行你的 Flutter 应用,服务器将会在 http://localhost:8080 上监听请求。

5. 测试会话管理

你可以使用浏览器或 HTTP 客户端(如 curl 或 Postman)来测试会话管理。

  • 第一次访问 http://localhost:8080 时,服务器会返回 Hello, Guest!,因为 username 尚未设置。
  • 在后续请求中,username 将会保留在会话中,直到会话过期或手动清除。

6. 自定义会话管理

你可以根据需要自定义会话的行为,例如设置会话过期时间、使用不同的存储后端(如内存、数据库等)。

var sessionManager = SessionManager(
  store: MemorySessionStore(), // 使用内存存储
  sessionTimeout: Duration(minutes: 30), // 设置会话过期时间为30分钟
);

7. 会话的清除与销毁

你可以通过 session.clear() 来清除会话中的所有数据,或者通过 session.destroy() 来销毁整个会话。

shelf.Response _handleLogout(shelf.Request request) {
  var session = request.context['session'] as Session;
  session.destroy();
  return shelf.Response.ok('Logged out successfully');
}
回到顶部