Authentication 如何向Scotty中间件添加基本身份验证?

Authentication 如何向Scotty中间件添加基本身份验证?,authentication,haskell,functional-programming,middleware,scotty,Authentication,Haskell,Functional Programming,Middleware,Scotty,我目前正在制作一个Scotty API,但找不到任何basicAuth实现(Wai中间件HttpAuth)的示例 具体地说,我想向我的一些端点(即以“admin”开头的端点)添加基本的auth头(user,pass)。我已经设置好了一切,但我似乎无法区分哪些端点需要auth,哪些端点不需要auth。我知道我需要使用类似的东西,但它使用的是Yesod,我无法将它翻译成Scotty 到目前为止,我有: routes :: (App r m) => ScottyT LText m () rout

我目前正在制作一个Scotty API,但找不到任何basicAuth实现(Wai中间件HttpAuth)的示例

具体地说,我想向我的一些端点(即以“admin”开头的端点)添加基本的auth头(user,pass)。我已经设置好了一切,但我似乎无法区分哪些端点需要auth,哪些端点不需要auth。我知道我需要使用类似的东西,但它使用的是Yesod,我无法将它翻译成Scotty

到目前为止,我有:

routes :: (App r m) => ScottyT LText m ()
routes = do
  -- middlewares
  middleware $ cors $ const $ Just simpleCorsResourcePolicy
    { corsRequestHeaders = ["Authorization", "Content-Type"]
    , corsMethods = "PUT":"DELETE":simpleMethods
    }
    
  middleware $ basicAuth 
      (\u p -> return $ u == "username" && p == "password") 
      "My Realm" 
  
  -- errors
  defaultHandler $ \str -> do
    status status500
    json str

  -- feature routes
  ItemController.routes
  ItemController.adminRoutes
  
  -- health
  get "/api/health" $
    json True
但它为我的所有请求添加了身份验证。我只需要其中一些


非常感谢你

您可以使用
AuthSettings
authIsProtected
字段定义一个函数
Request->IO Bool
,该函数确定特定(Wai)
请求
是否受基本身份验证的授权。特别是,您可以检查URL路径组件并通过这种方式进行确定

不幸的是,这意味着授权检查与Scotty路由完全分离。这在您的情况下工作得很好,但会使Scotty route难以对授权进行细粒度控制

无论如何,
AuthSettings
是源代码中重载的
“我的领域”
字符串,根据文档,建议定义设置的方法是使用重载字符串编写如下内容:

authSettings :: AuthSettings
authSettings = "My Realm" { authIsProtected = needsAuth }
这看起来很可怕,但无论如何,
needsAuth
函数将具有签名:

needsAuth :: Request -> IO Bool
因此,它可以检查Wai
请求
,并在IO中决定页面是否首先需要基本身份验证。在
请求
上调用
pathInfo
,可以得到路径组件的列表(没有主机名和查询参数)。因此,为了满足您的需要,以下几点应该有效:

needsAuth req = return $ case pathInfo req of
  "admin":_ -> True   -- all admin pages need authentication
  _         -> False  -- everything else is public
请注意,这些是已解析的非查询路径组件,因此
/admin
/admin/
/admin/whatever
甚至
/admin/?q=hello
都受到保护,但显然
/administrator/..
不受保护

一个完整的例子:

{-# LANGUAGE OverloadedStrings #-}

import Web.Scotty
import Network.Wai.Middleware.HttpAuth
import Data.Text ()   -- needed for "admin" overloaded string in case
import Network.Wai (Request, pathInfo)

authSettings :: AuthSettings
authSettings = "My Realm" { authIsProtected = needsAuth }

needsAuth :: Request -> IO Bool
needsAuth req = return $ case pathInfo req of
  "admin":_ -> True   -- all admin pages need authentication
  _         -> False  -- everything else is public

main = scotty 3000 $ do
  middleware $ basicAuth (\u p -> return $ u == "username" && p == "password") authSettings
  get "/admin/deletedb" $ do
    html "<h1>Password database erased!</h1>"
  get "/" $ do
    html "<h1>Homepage</h1><p>Please don't <a href=/admin/deletedb>Delete the passwords</a>"
{-#语言重载字符串}
导入Web.Scotty
导入Network.Wai.Middleware.HttpAuth
import Data.Text()--需要“admin”重载字符串以防
导入Network.Wai(请求,路径信息)
authSettings::authSettings
authSettings=“我的领域”{authIsProtected=needsAuth}
needsAuth::请求->IO布尔
needsAuth req=返回$case pathInfo req of
“admin”:->True--所有管理员页面都需要身份验证
_->错误--其他一切都是公开的
main=scotty 3000$do
中间件$basicAuth(\u p->return$u==“username”&&p==“password”)身份验证设置
获取“/admin/deletedb”$do
html“密码数据库已删除!”
获取“/”$do
html“主页请不要”

非常感谢您!你的回答非常清楚,而且非常有效:)