HTTP确认访问用户身份的认证

你未必出类拔萃,但一定与众不同

HTTP确认访问用户身份的认证

  • BASIC认证
  • DIGEST认证
  • SSL客户端认证
  • FormBase认证

1.BASIC认证

概述

BASIC认证是从HTTP1.0就定义的一种WEB服务器与通信客户端之间进行的认证方式,又称为基本认证

认证步骤

在这里插入图片描述

  • 当请求的资源需要进行BASIC认证时,服务器会随状态码401Authorization Required,返回带WWW-Authorization首部字段的响应,该字段内包含认证的方式以及Request-URI安全域字符串
  • 接受到状态码401的客户端为了通过BASIC认证,需要将ID和密码发送给服务器,发送的内容是ID:密码然后通过Base64编码处理
  • 接受到包含首部字段的Authorization请求的服务器,会对认证信息的正确性进行验证,验证通过则返回一条包含Request-URI资源的响应

下图就是对接受到的Authorization字段进行处理,只不过Authorization中存储的是当前请求生成的SessonId进行校验

public class MySessionManager extends DefaultWebSessionManager {

    /**
     * 日志记录
     */
    private final Logger logger =  LoggerFactory.getLogger(this.getClass());
    private static final String AUTHORIZATION = "Authorization";

    private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";

    public MySessionManager() {
        super();
    }

    @Override
    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
        String id = WebUtils.toHttp(request).getHeader(AUTHORIZATION);
        //如果请求头中有 Authorization 则其值为sessionId
        if (!StringUtils.isEmpty(id)) {
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
            return id;
        } else {
            //否则按默认规则从cookie取sessionId
            return super.getSessionId(request, response);
        }
    }
}

缺点

  • 虽然采用Base64进行处理,但是不是加密处理,不需要附加信息进行解密
  • 无法实现认证注销操作
  • 认证使用上不够灵活

2.DIGEST认证

概述

为了弥补BASIC认证的缺点,HTTP在1.1开始就有了DIGEST认证

DIGEST认证同样采用质询/相应的方式,但是不会像BASIC认证认证一样发送明文

质询方式

一开始一方会先发送认证要求给另一方,接着使用从另一方那接受到的质询码计算生成响应码,最后返回响应码的认证方式。

发送给对方的只是响应摘要以及质询码产生的计算结果,比起BASIC认证,密码泄露的可能性降低了

认证步骤

在这里插入图片描述

  • 请求需要认证的资源,服务器会随状态码401Authorization Required,返回带WWW-Authorization首部字段的响应,该字段包含质问响应方式认证所需的临时质询码(随机数)

    首部字段WWW-Authorization内必须包含realm和nonce这两个字段的信息,客户端就是依靠向服务器回送这两个值进行认证

  • 接受到401状态码的客户端,返回的响应中包含DIGEST认证必须的首部字段Authorization信息

    Authorization必须包含username,realm,nonce,uri,response

  • 接收到包含首部字段Authorization请求的服务器,会先确认认证信息的正确性,认证通过后则返回包含Reques-URI资源的响应。

缺点

  • 不存在防止用户伪装的保护机制
  • 安全等级比不上HTTPS的客户端认证

3.SSL客户端认证

SSL客户端认证借由HTTPS 的客户端认证起效

HTTPS的认证过程看这篇确保安全的HTTPS

认证步骤

  • 接受到需要认证资源的请求,服务器会发送Certificate Request报文,要求客户端提供客户端证书
  • 用户选择将发送的客户端证书后,客户端会把客户端证书信息以Client Certificate报文方式发送给服务器。
  • 服务器验证客户端证书验证通过之后方可领取证书内容客户端的公开秘钥,然后开始HTTPS加密通信

SSL客户端一般采用双因素认证

  • SSL客户端证书用来认证客户端计算机
  • 密码用来确认用户的行为

缺点

  • 证书需要付费,耗费较大

4.FormBase认证

基于表单认证,目前市面上大多数都是基于表单认证。

认证过程

在这里插入图片描述

  • 客户端把一些用户信息登录信息放入报文的实体部分,通常使用POST方式,然后使用HTTPS通信来进行表单画面的显示以及用户输入的方式
  • 服务器会发放用以识别用户的SessionId 通过验证从客户端发送过来的登录信息进行身份认证,然后把用户的认证状态与SessionId绑定后记录在服务器端
  • 客户端接收到SessionId 将其视为Cookie保存在本地,下次发送请求的时候,在带入Cookie

实践应用

微信小程序发送请求的时候可能不会带入cookie,因此采用表单认证,我们自定义创建一个表单认证

小程序js Authorization存储的就是当前的sessionId,不过这个是我们返回的

// post请求封装
export function postRequest(url, data) {
	var promise = new Promise((resolve, reject) => {
		var that = this;
		var postData = data;
		uni.request({
			url: commonUrl + url,
			data: postData,
			method: "POST",
			header: {
				"content-type": "application/x-www-form-urlencoded",
				Authorization: uni.getStorageSync("sessionId")
			},
			success: function(res) {
				//返回什么就相应的做调整
				if (res.statusCode == 200) {
					resolve(res.data);
				} else {
					// 请求服务器成功,但是由于服务器没有数据返回,此时无code。会导致这个空数据
					//接口后面的then执行
					// 不下去,导致报错,所以还是要resolve下,这样起码有个返回值,
					//不会被阻断在那里执行不下去!
					resolve(res.data.msg);
				}
			},
			error: function(e) {
				reject("网络出错");
			}
		});
	});
	return promise;
}

后端SpringBoot 采用shiro进行校验认证

自定义一个Session管理器,用来对小程序发送的前端Header中的sessionId进行取值

public class MySessionManager extends DefaultWebSessionManager {

    /**
     * 日志记录
     */
    private final Logger logger =  LoggerFactory.getLogger(this.getClass());
    private static final String AUTHORIZATION = "Authorization";

    private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";

    public MySessionManager() {
        super();
    }

    @Override
    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
        String id = WebUtils.toHttp(request).getHeader(AUTHORIZATION);
        //如果请求头中有 Authorization 则其值为sessionId
        if (!StringUtils.isEmpty(id)) {
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
            return id;
        } else {
            //否则按默认规则从cookie取sessionId
            return super.getSessionId(request, response);
        }
    }
}

在进行第一次登陆的时候

HttpSession session = request.getSession();
String sessionId = session.getId();
................
登陆业务成功以后,返回一个我们生成的sessionId
resultMap.setSessionId(sessionId);

resultMap是我们自定义的一个返回标准类

然后将我们定义的sessionManager注册进去shiro

/**
 * 自定义sessionManager
 * @return
 */
@Bean
public SessionManager sessionManager() {
    MySessionManager mySessionManager = new MySessionManager();
    return mySessionManager;
}
posted @ 2021-06-07 11:08  万里江海阔  阅读(76)  评论(0)    收藏  举报