Ruby on Rails 5 API模式下能否在路由约束中使用HTTP基本认证?
如何在Rails路由约束中正确实现HTTP基本认证?
你遇到的NoMethodError是因为authenticate_or_request_with_http_basic是ActionController模块提供的方法,而你自定义的Basic约束类并没有混入这个模块,直接调用肯定会报错。下面给你两种可行的解决方案,还顺便纠正你代码里的一个小坑~
方案一:基础验证(不通过则返回404)
这种方式会在认证失败时让路由不匹配,用户访问对应路径会得到404。修改你的constraints/basic.rb:
class Basic def self.matches?(request) # 从请求头中解码基本认证信息 credentials = ActionController::HttpAuthentication::Basic.decode_credentials(request) return false unless credentials # 拆分账号和密码(用split(': ', 2)避免密码带冒号的情况) email, password = credentials.split(':', 2) # 注意!你之前写的是password = 'bar'(赋值),应该是==(判断) email == 'foo' && password == 'bar' end end
方案二:返回401未授权(更符合HTTP规范)
如果希望认证失败时返回标准的401响应(而不是404),可以手动构造响应头和响应内容:
class Basic def self.matches?(request) # 用ActionController的工具方法验证账号密码 authenticated = ActionController::HttpAuthentication::Basic.authenticate(request) do |email, password| email == 'foo' && password == 'bar' end unless authenticated # 设置WWW-Authenticate头,让浏览器弹出认证框 request.headers['WWW-Authenticate'] = ActionController::HttpAuthentication::Basic.authenticate_header(request, 'Protected Area') # 构造401响应 request.env['rack.response'] = [ 401, {'Content-Type' => 'text/plain'}, ['HTTP Basic: Access denied.'] ] end authenticated end end
补充说明
- 路由约束的核心作用是判断请求是否匹配当前路由规则,所以方案一的逻辑更贴合约束的设计;但如果需要明确告诉用户“未授权”而不是“路径不存在”,方案二更友好。
- 务必修正你代码里的低级错误:
password = 'bar'是赋值操作,永远会返回'bar',导致验证逻辑完全失效,一定要改成==!
内容的提问来源于stack exchange,提问作者Eugene Yak
相关产品推荐
相关产品推荐

