Guardian实际案例解析:在电商平台中实现用户认证系统的10个关键步骤

【免费下载链接】guardian Elixir Authentication 【免费下载链接】guardian 项目地址: https://gitcode.com/gh_mirrors/gu/guardian

Guardian是一个功能强大的Elixir身份验证库,专为构建安全的Web应用程序而设计。在电商平台开发中,用户认证系统是核心功能之一,而Guardian提供了完整的基于令牌的身份验证解决方案,特别适合现代电商应用的分布式架构需求。本文将深入解析如何在电商平台中实现基于Guardian的用户认证系统,涵盖从基础配置到高级权限管理的完整流程。

为什么选择Guardian构建电商认证系统? 🔐

电商平台对用户认证系统有着特殊要求:高并发处理能力、安全的数据传输、灵活的权限管理以及良好的用户体验。Guardian作为Elixir生态中最受欢迎的身份验证库,提供了以下核心优势:

  • 基于JWT的令牌认证:无状态认证机制,适合分布式电商系统架构
  • 灵活的权限管理:支持细粒度权限控制,满足电商平台的复杂权限需求
  • 完整的Plug集成:与Phoenix框架无缝集成,简化Web应用开发
  • 多令牌类型支持:支持访问令牌、刷新令牌等多种令牌类型
  • 易于扩展:模块化设计,可根据电商业务需求灵活定制

电商平台认证系统架构设计 🏗️

在电商平台中,认证系统通常需要处理以下场景:

  1. 用户注册与登录:基础的身份验证流程
  2. 购物车状态保持:用户登录状态的持久化
  3. 支付安全验证:敏感操作的双重认证
  4. API访问控制:第三方应用接入的权限管理
  5. 管理员后台权限:多层级管理员权限控制

10步实现电商平台认证系统 📋

1. 创建电商用户认证模块

首先,我们需要创建一个专门的Guardian实现模块来处理电商用户的认证逻辑:

defmodule EcommercePlatform.UserAuth do
  use Guardian, otp_app: :ecommerce_platform
  
  alias EcommercePlatform.Accounts.User
  alias EcommercePlatform.Repo

  # 将用户转换为令牌主题
  def subject_for_token(%User{id: id}, _claims) do
    {:ok, to_string(id)}
  end

  # 从令牌声明中恢复用户
  def resource_from_claims(%{"sub" => id}) do
    case Repo.get(User, id) do
      nil -> {:error, :resource_not_found}
      user -> {:ok, user}
    end
  end

  # 添加自定义声明(如用户角色)
  def build_claims(claims, user, _opts) do
    claims = Map.put(claims, "role", user.role)
    claims = Map.put(claims, "email", user.email)
    {:ok, claims}
  end
end

2. 配置电商专用权限系统

电商平台需要复杂的权限管理,Guardian的权限系统可以轻松应对:

defmodule EcommercePlatform.UserAuth do
  use Guardian, otp_app: :ecommerce_platform,
    permissions: %{
      default: [
        :browse_products,
        :add_to_cart,
        :place_order
      ],
      admin: %{
        manage_products: 0b1,
        manage_orders: 0b10,
        manage_users: 0b100,
        manage_promotions: 0b1000
      },
      seller: %{
        manage_own_products: 0b1,
        view_own_sales: 0b10,
        manage_own_inventory: 0b100
      }
    }
  
  use Guardian.Permissions, encoding: Guardian.Permissions.BitwiseEncoding

  # 在构建令牌时编码权限
  def build_claims(claims, user, opts) do
    perms = Keyword.get(opts, :permissions, %{})
    claims = encode_permissions_into_claims!(claims, perms)
    {:ok, claims}
  end
end

3. 设置电商平台认证管道

电商平台需要多种认证管道来处理不同场景:

defmodule EcommercePlatform.AuthPipeline do
  use Guardian.Plug.Pipeline,
    otp_app: :ecommerce_platform,
    module: EcommercePlatform.UserAuth,
    error_handler: EcommercePlatform.AuthErrorHandler

  # API认证管道
  plug Guardian.Plug.VerifyHeader, realm: "Bearer"
  plug Guardian.Plug.LoadResource, allow_blank: true

  # Web会话认证管道
  plug Guardian.Plug.VerifySession
  plug Guardian.Plug.LoadResource, allow_blank: true
end

defmodule EcommercePlatform.AdminAuthPipeline do
  use Guardian.Plug.Pipeline,
    otp_app: :ecommerce_platform,
    module: EcommercePlatform.UserAuth,
    error_handler: EcommercePlatform.AuthErrorHandler

  plug Guardian.Plug.VerifySession
  plug Guardian.Plug.VerifyHeader, realm: "Bearer"
  plug Guardian.Plug.EnsureAuthenticated
  plug Guardian.Plug.LoadResource
  plug Guardian.Permissions, ensure: %{admin: [:manage_users]}
end

4. 实现电商用户会话管理

电商平台的会话管理需要特殊处理,特别是购物车状态保持:

defmodule EcommercePlatformWeb.SessionController do
  use EcommercePlatformWeb, :controller
  
  alias EcommercePlatform.Accounts
  alias EcommercePlatform.UserAuth

  def login(conn, %{"email" => email, "password" => password}) do
    case Accounts.authenticate_user(email, password) do
      {:ok, user} ->
        # 为用户生成访问令牌和刷新令牌
        {:ok, access_token, _claims} = 
          UserAuth.encode_and_sign(user, %{}, token_type: "access", ttl: {15, :minutes})
        
        {:ok, refresh_token, _claims} = 
          UserAuth.encode_and_sign(user, %{}, token_type: "refresh", ttl: {7, :days})
        
        # 设置会话和cookie
        conn
        |> put_session(:user_id, user.id)
        |> put_resp_cookie("refresh_token", refresh_token, 
            max_age: 7 * 24 * 60 * 60, http_only: true)
        |> json(%{
          access_token: access_token,
          expires_in: 900,
          user: %{
            id: user.id,
            email: user.email,
            name: user.name
          }
        })
      
      {:error, reason} ->
        conn
        |> put_status(:unauthorized)
        |> json(%{error: "Invalid credentials"})
    end
  end

  def refresh_token(conn, _params) do
    with refresh_token <- get_req_header(conn, "refresh_token") || 
                          conn.cookies["refresh_token"],
         {:ok, claims} <- UserAuth.decode_and_verify(refresh_token, %{"typ" => "refresh"}),
         {:ok, user} <- UserAuth.resource_from_claims(claims),
         {:ok, _old_stuff, {new_token, _new_claims}} <- 
           UserAuth.exchange(refresh_token, "refresh", "access") do
      
      conn
      |> json(%{
        access_token: new_token,
        expires_in: 900
      })
    else
      _ ->
        conn
        |> put_status(:unauthorized)
        |> json(%{error: "Invalid refresh token"})
    end
  end
end

5. 购物车状态同步机制

电商平台需要确保用户登录后购物车状态的正确同步:

defmodule EcommercePlatformWeb.CartController do
  use EcommercePlatformWeb, :controller
  
  plug EcommercePlatform.AuthPipeline
  
  def merge_cart(conn, _params) do
    user = Guardian.Plug.current_resource(conn)
    anonymous_cart_id = get_session(conn, :anonymous_cart_id)
    
    if anonymous_cart_id do
      # 合并匿名购物车到用户购物车
      EcommercePlatform.Carts.merge_carts(anonymous_cart_id, user.id)
      
      conn
      |> delete_session(:anonymous_cart_id)
      |> json(%{message: "Cart merged successfully"})
    else
      json(conn, %{message: "No cart to merge"})
    end
  end
  
  def get_cart(conn, _params) do
    user = Guardian.Plug.current_resource(conn)
    
    if user do
      cart = EcommercePlatform.Carts.get_user_cart(user.id)
      json(conn, %{cart: cart})
    else
      anonymous_cart_id = get_session(conn, :anonymous_cart_id) ||
                          EcommercePlatform.Carts.create_anonymous_cart()
      
      conn
      |> put_session(:anonymous_cart_id, anonymous_cart_id)
      |> json(%{cart: EcommercePlatform.Carts.get_cart(anonymous_cart_id)})
    end
  end
end

6. 支付安全验证增强

电商支付需要额外的安全验证层:

defmodule EcommercePlatform.PaymentAuth do
  use Guardian, otp_app: :ecommerce_platform
  
  # 支付专用令牌,有效期短
  def subject_for_token(%{payment_id: payment_id}, _claims) do
    {:ok, "payment_#{payment_id}"}
  end

  def resource_from_claims(%{"sub" => "payment_" <> payment_id}) do
    case EcommercePlatform.Payments.get_payment(payment_id) do
      nil -> {:error, :payment_not_found}
      payment -> {:ok, payment}
    end
  end

  # 支付令牌额外验证
  def verify_claims(claims, _opts) do
    with true <- Map.get(claims, "payment_verified", false),
         true <- Map.get(claims, "amount_verified", false) do
      {:ok, claims}
    else
      _ -> {:error, :payment_verification_failed}
    end
  end
end

7. API访问控制与速率限制

电商平台API需要精细的访问控制:

defmodule EcommercePlatformWeb.APIAuthPipeline do
  use Guardian.Plug.Pipeline,
    otp_app: :ecommerce_platform,
    module: EcommercePlatform.UserAuth,
    error_handler: EcommercePlatform.APIErrorHandler

  plug Guardian.Plug.VerifyHeader, realm: "Bearer"
  plug Guardian.Plug.EnsureAuthenticated
  plug Guardian.Plug.LoadResource
  
  # API速率限制
  plug EcommercePlatform.RateLimiter,
    max_requests: 100,
    time_window: {1, :minute}
  
  # API权限检查
  plug Guardian.Permissions,
    ensure: %{default: [:api_access]}
end

defmodule EcommercePlatform.RateLimiter do
  import Plug.Conn
  
  def init(opts), do: opts
  
  def call(conn, opts) do
    user = Guardian.Plug.current_resource(conn)
    api_key = get_req_header(conn, "x-api-key")
    
    limit_key = if user, do: "user_#{user.id}", else: "api_#{api_key}"
    
    case check_rate_limit(limit_key, opts) do
      {:ok, _} -> conn
      {:error, :rate_limited} ->
        conn
        |> put_resp_header("retry-after", "60")
        |> put_status(429)
        |> halt()
    end
  end
  
  defp check_rate_limit(_key, _opts) do
    # 实现基于Redis或Ets的速率限制逻辑
    {:ok, :allowed}
  end
end

8. 多商户权限管理系统

电商平台通常需要支持多商户,每个商户有自己的管理员:

defmodule EcommercePlatform.MerchantAuth do
  use Guardian, otp_app: :ecommerce_platform
  
  alias EcommercePlatform.Accounts.MerchantUser
  
  def subject_for_token(%MerchantUser{id: id, merchant_id: merchant_id}, _claims) do
    {:ok, "merchant_#{merchant_id}_user_#{id}"}
  end

  def resource_from_claims(%{"sub" => "merchant_" <> subject}) do
    [merchant_id, "user", user_id] = String.split(subject, "_")
    
    case EcommercePlatform.Accounts.get_merchant_user(
           String.to_integer(merchant_id), 
           String.to_integer(user_id)
         ) do
      nil -> {:error, :resource_not_found}
      merchant_user -> {:ok, merchant_user}
    end
  end

  def build_claims(claims, merchant_user, _opts) do
    claims
    |> Map.put("merchant_id", merchant_user.merchant_id)
    |> Map.put("merchant_role", merchant_user.role)
    |> Map.put("permissions", get_merchant_permissions(merchant_user))
  end
  
  defp get_merchant_permissions(merchant_user) do
    # 根据商户角色获取权限
    case merchant_user.role do
      :owner -> [:manage_all, :view_reports, :manage_products]
      :manager -> [:view_reports, :manage_products, :manage_orders]
      :staff -> [:view_products, :manage_orders]
      _ -> []
    end
  end
end

9. 实时通知与WebSocket认证

电商平台需要实时通知功能,Guardian支持WebSocket认证:

defmodule EcommercePlatformWeb.UserSocket do
  use Phoenix.Socket
  
  channel "user:*", EcommercePlatformWeb.UserChannel
  channel "order:*", EcommercePlatformWeb.OrderChannel
  
  def connect(%{"token" => token}, socket, _connect_info) do
    case EcommercePlatform.UserAuth.decode_and_verify(token) do
      {:ok, claims} ->
        case EcommercePlatform.UserAuth.resource_from_claims(claims) do
          {:ok, user} ->
            socket = assign(socket, :user_id, user.id)
            {:ok, socket}
          {:error, _reason} ->
            :error
        end
      {:error, _reason} ->
        :error
    end
  end
  
  def connect(_params, _socket, _connect_info), do: :error
  
  def id(socket), do: "user_socket:#{socket.assigns.user_id}"
end

defmodule EcommercePlatformWeb.UserChannel do
  use Phoenix.Channel
  
  intercept ["order_updated", "payment_success"]
  
  def join("user:" <> user_id, _message, socket) do
    if socket.assigns.user_id == String.to_integer(user_id) do
      {:ok, socket}
    else
      {:error, %{reason: "unauthorized"}}
    end
  end
  
  def handle_out("order_updated", payload, socket) do
    push(socket, "order_updated", payload)
    {:noreply, socket}
  end
end

10. 安全监控与审计日志

电商平台需要完善的安全监控:

defmodule EcommercePlatform.SecurityAudit do
  use GenServer
  
  # Guardian回调:令牌创建后记录
  def after_encode_and_sign(resource, claims, token, opts) do
    :ok = log_audit_event(:token_created, %{
      user_id: resource.id,
      token_type: Map.get(claims, "typ"),
      ip_address: get_ip_from_opts(opts),
      timestamp: DateTime.utc_now()
    })
    {:ok, token}
  end
  
  # Guardian回调:令牌验证后记录
  def on_verify(claims, token, opts) do
    :ok = log_audit_event(:token_verified, %{
      user_id: claims["sub"],
      token_type: claims["typ"],
      ip_address: get_ip_from_opts(opts),
      timestamp: DateTime.utc_now()
    })
    {:ok, claims}
  end
  
  # Guardian回调:令牌撤销后记录
  def on_revoke(claims, token, opts) do
    :ok = log_audit_event(:token_revoked, %{
      user_id: claims["sub"],
      token_type: claims["typ"],
      ip_address: get_ip_from_opts(opts),
      timestamp: DateTime.utc_now()
    })
    {:ok, claims}
  end
  
  defp log_audit_event(event_type, data) do
    # 实现审计日志记录逻辑
    # 可以记录到数据库、文件或外部日志服务
    :ok
  end
  
  defp get_ip_from_opts(opts) do
    Keyword.get(opts, :ip_address, "unknown")
  end
end

电商平台认证最佳实践 🏆

令牌生命周期管理

  • 短期访问令牌:15-30分钟有效期,减少安全风险
  • 长期刷新令牌:7-30天有效期,存储在HttpOnly Cookie中
  • 自动令牌刷新:在访问令牌过期前自动使用刷新令牌获取新令牌

安全增强措施

  • HTTPS强制使用:所有认证请求必须使用HTTPS
  • CSRF保护:对状态变更请求实施CSRF令牌验证
  • XSS防护:设置合适的CSP策略和HttpOnly Cookie
  • 速率限制:防止暴力破解攻击

性能优化策略

  • 令牌缓存:常用令牌的短期内存缓存
  • 权限预加载:用户权限的批量预加载
  • 分布式会话:使用Redis等分布式存储管理会话状态

故障排除与调试技巧 🛠️

常见问题解决

  1. 令牌验证失败:检查令牌签名、过期时间和权限设置
  2. 权限不足:验证用户角色和权限配置
  3. 会话丢失:检查Cookie设置和跨域配置
  4. 性能问题:优化数据库查询和令牌验证逻辑

调试工具

  • 使用Guardian.peek/1检查令牌内容而不验证
  • 启用详细日志记录认证流程
  • 使用测试令牌进行集成测试

总结与展望 🚀

通过Guardian构建的电商平台认证系统不仅提供了强大的安全保障,还具备了良好的扩展性和灵活性。随着电商业务的增长,认证系统可以轻松扩展支持:

  • OAuth2第三方登录:集成微信、支付宝等第三方登录
  • 多因素认证:增加短信验证、生物识别等认证方式
  • 风险控制:基于用户行为的动态权限调整
  • 合规性支持:满足GDPR、PCI DSS等法规要求

Guardian的模块化设计使得这些扩展变得简单而优雅。通过合理利用Guardian提供的各种功能,电商平台可以构建出既安全又用户友好的认证系统,为业务增长提供坚实的技术基础。

相关资源 📚

通过本文的10个关键步骤,您可以快速在电商平台中实现基于Guardian的完整用户认证系统,为您的电商业务提供安全可靠的身份验证保障。

【免费下载链接】guardian Elixir Authentication 【免费下载链接】guardian 项目地址: https://gitcode.com/gh_mirrors/gu/guardian

Logo

电商企业物流数字化转型必备!快递鸟 API 接口,72 小时快速完成物流系统集成。全流程实战1V1指导,营造开放的API技术生态圈。

更多推荐