浏览器-基本认证(Basic Authentication)-摘要认证(digest authentication)=spring boot实现demo

本文主要是介绍浏览器-基本认证(Basic Authentication)-摘要认证(digest authentication)=spring boot实现demo,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

平时开发的 java web 网站登录,都是通过表单提交登录信息。有时一些中间件登录是浏览器弹窗,没有看到表单实现代码。故通过查询,发现两种 HTTP 简单认证: 基本认证( Basic Authentication )、摘要认证( digest authentication )等,本次通过 java实现 spring boot 基本和摘要认证。
基本认证

  1. 客户端发送 HTTP 给服务器;
  2. 发送 Request 中 header 没含Authorization, 服务器返回401 Unauthozied ,并在Response 的 header中WWW-Authenticate添加信息(Basic realm="自定义内容");
  3. 客户端把用户名和密码(用户名+冒号+密码)用 BASE64 编码后,放在 Request 中 header Authorization 发送给服务器, 认证成功
  4. 服务器将 header 中Authorization的用户名密码取出验证, 如果验证通过,将根据请求,返回资源

摘要认证

  1. 客户端发送 HTTP 给服务器;
  2. 发送 Request 中 header 没含Authorization, 服务器产生(Digest 、认证的域realm="自定义内容"、认证的校验方式qop、随机数nonce、随机字符opaque、认证算法algorithm)字符串拼接一起,放在WWW-Authenticate响应头,一起发送给客户端;
  3. 客户端发现401响应,表示需认证,则弹出让用户输入用户名和密码的认证窗口,客户端选择算法,计算出密码和其他数据的摘要(response),放到Authorization的请求头中发送服务器,如客户端要对服务器也进行认证,这时可发送客户端随机数cnonce
  4. 服务接受摘要,选择算法,获取存储的用户名密码,计算摘要跟客户端传输的摘要进行比较,验证是否匹配

参考

HTTP基本认证(Basic Authentication)的JAVA示例
摘要认证及实现HTTP digest authentication
JAVA 调用 RFC2617摘要认证协议的接口
curl命令使用digest方式验证用户
http请求头和响应头
Java猿社区—Http digest authentication 请求代码最全示例
RFC 2617 - HTTP Authentication: Basic and Digest Access Authenti
HTTP Authentication: Basic and Digest Access Authentication
C# 摘要认证(digest authentication) IETF RFC 2617
http协议基本认证 Authorization
中文rfc2617-001
Java 基于 IETF RFC 2617 身份认证

代码

pom

<properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><spring-boot.version>2.3.1.RELEASE</spring-boot.version>
</properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-core</artifactId><version>5.3.8</version></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-crypto</artifactId><version>5.3.8</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>
</dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency></dependencies>
</dependencyManagement>

接口代码


import cn.hutool.core.lang.UUID;
import org.apache.tomcat.util.security.MD5Encoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;/*** 测试认证接口** @author z.y.l* @version v1.0* @date 2020-09-10*/
@Controller
@RequestMapping("/auth")
public class AuthController {private final Logger logger = LoggerFactory.getLogger(AuthController.class);private final static String AUTH_B = "Basic ",AUTH_D = "Digest ";@GetMapping ("")@ResponseBodypublic String root(){return "Hello Spring Boot Auth Demo.By zyl.";}@GetMapping ("basic/info")@ResponseBodypublic String basicInfo(){return "Hello Basic Authentication.By zyl.";}@RequestMapping("basic")@ResponseBodypublic String basic(HttpServletRequest request, HttpServletResponse response){//基本认证(Basic Authentication)String auth = request.getHeader("Authorization");if( null == auth || auth.isEmpty() || !auth.startsWith(AUTH_B)){logger.info("测试浏览器原生弹窗登录认证-未登录");response.addHeader("WWW-Authenticate","Basic realm=\".\"");response.setStatus(401);//缓存不应存储有关客户端请求或服务器响应的任何内容,即不使用任何缓存response.setHeader("Cache-Control", "no-store");//0, 代表着过去的日期,即该资源已经过期response.setDateHeader("Expires", 0);}else {logger.info("测试浏览器原生弹窗登录认证-验证登录");logger.info("auth, {}",auth);auth = new String(Base64.getDecoder().decode(auth.substring(AUTH_B.length())));logger.info("auth-decode, {}",auth);String[] split = auth.split(":");String n = split[0],p = split[1];logger.info("auth-decode, name: {} , p: {}",n,p);}return "Hello Spring Boot Demo.by auth.";}@RequestMapping("digest")@ResponseBodypublic String digest(HttpServletRequest request, HttpServletResponse response){//摘要认证 digest authentication/*摘要认证及实现HTTP digest authentication https://www.jianshu.com/p/cf5a900d4ef7*/String auth = request.getHeader("Authorization");if( null == auth || auth.isEmpty() || !auth.startsWith(AUTH_D)){logger.info("测试浏览器原生弹窗登录认证-摘要认证-未登录");Map<String, String> map = new HashMap<>(5);//值是一个简单的字符串map.put("realm","realm");//保护质量,是认证的(校验)方式.auth表示认证;auth-int表示完整性保护认证map.put("qop","auth,auth-int");//是随机数, 可以用GUIDmap.put("nonce",UUID.fastUUID().toString(true));//是个随机字符串,它只是透传而已,即客户端还会原样返回过来。map.put("opaque",UUID.fastUUID().toString(true));//是个字符串,用来指示用来产生分类及校验和的算法对。如果该域没指定,则认为是“MD5“算法。map.put("algorithm","MD5");StringBuilder sb = new StringBuilder(AUTH_D);map.forEach((k,v) -> sb.append(k).append('=').append('"').append(v).append('"').append(','));response.addHeader("WWW-Authenticate",sb.toString());logger.info("WWW-Authenticate: {}",sb.toString());response.setStatus(401);}else {logger.info("测试浏览器原生弹窗登录认证-摘要认证-验证登录");logger.info("auth, {}",auth);String[] split = auth.split(", ");String nc = "",cnonce="",nonce="",qop="",algorithm="";for (String sp : split) {logger.info("auth-ap, {}",sp);if(sp.startsWith("nc")){nc = sp.substring("nc=".length());}else if(sp.startsWith("cnonce")){cnonce = sp.substring("cnonce=".length());}else if(sp.startsWith("nonce")){nonce = sp.substring("nonce=".length());}else if(sp.startsWith("qop")){qop = sp.substring("qop=".length());}else if(sp.startsWith("algorithm")){algorithm = sp.substring("algorithm=".length());}}String name = "asd", realm = "realm",pass = "123456";String a1 = name+":"+realm+":"+pass;MD5 md_5 = MD5.create();String ha1 = md_5.digestHex(a1,"UTF-8");logger.info("MD5 name:realm:pass > {} > {}",a1,ha1);boolean sess = "MD5-sess".equalsIgnoreCase(algorithm);if(sess){ha1 = ha1 + ":" + nonce + ":" + cnonce;logger.info("MD5-sess md5(name:realm:pass):nonce:cnonce > {} > {}",a1,ha1);}String a2 = "get:/hello/digest";String ha2 = md_5.digestHex(a2,"UTF-8");logger.info(" method:uri > {} > {}",a2,ha2);if("auth-int".equals(qop)){logger.info("如果 qop 值为“auth-int”,那么 HA2 为:HA2=MD5(A2)=MD5(method:digestURI:MD5(entityBody)),实现代码未找到");}if("auth".equalsIgnoreCase(qop) || "auth-int".equalsIgnoreCase(qop)){String md = ha1+":"+nonce+":"+nc+":"+cnonce+":"+qop+":"+ha2;String md5 = md_5.digestHex(md,"UTF-8");logger.info("response ha1:nonce:nc:cnonce:qop:ha2 > {} > {}",md,md5);}else{String md = ha1+":"+nonce+":"+ha2;String md5 = md_5.digestHex(md,"UTF-8");logger.info("response ha1:nonce:ha2 > {} > {}",md,md5);}}return "Hello Spring Boot Demo.by digest.";}@GetMapping ("digest/info")@ResponseBodypublic String digestInfo(){return "Hello Digest Authentication.By zyl.";}
}

测试

请求:http://localhost:8080/auth
在这里插入图片描述

基本认证

浏览器访问

请求:http://localhost:8080/auth/basic,浏览器提示登录,请求报文显示了后端响应内容
在这里插入图片描述
输入账号密码登陆后,请求头会携带认证信息
在这里插入图片描述
日志
在这里插入图片描述

[AuthController.java:62] =.= 测试浏览器原生弹窗登录认证-验证登录
[AuthController.java:63] =.= auth, Basic YWRtaW46MTIzNDU2
[AuthController.java:65] =.= auth-decode, admin:123456
[AuthController.java:68] =.= auth-decode, name: admin , p: 123456

请求:http://localhost:8080/auth/basic/info,就会携带认证信息
在这里插入图片描述

Linux使用curl访问

在Linux服务器通过curl访问认证接口

curl -u test http://192.168.xxx.xxx:8080/auth/basic
Enter host password for user 'test':
Hello Spring Boot Demo.by auth.

在这里插入图片描述
接口打印日志
在这里插入图片描述

[AuthController.java:54] =.= 测试浏览器原生弹窗登录认证-验证登录
[AuthController.java:55] =.= auth, Basic dGVzdDo2NTQzMjE=
[AuthController.java:57] =.= auth-decode, test:654321
[AuthController.java:60] =.= auth-decode, name: test , p: 654321

摘要认证

浏览器访问

请求:http://localhost:8080/auth/digest,浏览器提示登录
在这里插入图片描述
输入账号密码登陆后,请求头会携带认证信息
在这里插入图片描述
在这里插入图片描述

[AuthController.java:91] =.= 测试浏览器原生弹窗登录认证-摘要认证-验证登录
[AuthController.java:92] =.= auth, Digest username="asd", realm="realm", nonce="d8df2efaecf74dd8a736147794eb2aff", uri="/auth/digest", algorithm=MD5, response="bfcdf3e596dace72c035de009f96458b", opaque="b4bccdce4a6b4ba1a42acf3d357c33bc", qop=auth, nc=00000004, cnonce="b2596a7472eee0df"
[AuthController.java:96] =.= auth-ap, Digest username="asd"
[AuthController.java:96] =.= auth-ap, realm="realm"
[AuthController.java:96] =.= auth-ap, nonce="d8df2efaecf74dd8a736147794eb2aff"
[AuthController.java:96] =.= auth-ap, uri="/auth/digest"
[AuthController.java:96] =.= auth-ap, algorithm=MD5
[AuthController.java:96] =.= auth-ap, response="bfcdf3e596dace72c035de009f96458b"
[AuthController.java:96] =.= auth-ap, opaque="b4bccdce4a6b4ba1a42acf3d357c33bc"
[AuthController.java:96] =.= auth-ap, qop=auth
[AuthController.java:96] =.= auth-ap, nc=00000004
[AuthController.java:96] =.= auth-ap, cnonce="b2596a7472eee0df"
[AuthController.java:113] =.= MD5 name:realm:pass > asd:realm:123456 > 41fd06d7b3a49c6fc0d44d338b3b2fb8
[AuthController.java:123] =.=  method:uri > get:/hello/digest > a422e85fbdc5fb6cb7d362a4207ff531
[AuthController.java:131] =.= response ha1:nonce:nc:cnonce:qop:ha2 > 41fd06d7b3a49c6fc0d44d338b3b2fb8:"d8df2efaecf74dd8a736147794eb2aff":00000004:"b2596a7472eee0df":auth:a422e85fbdc5fb6cb7d362a4207ff531 > f5faaf2b18a4ed555cdec05a02454398

请求:http://localhost:8080/auth/digest/info,就会携带认证信息
在这里插入图片描述

Linux使用curl访问

在Linux服务器通过curl访问认证接口

curl -i http://192.168.xxx.xxx:8080/auth/digest -X GET --digest --user asd:12345
HTTP/1.1 401 
WWW-Authenticate: Digest realm="realm",qop="auth,auth-int",opaque="0f7db485912f42d5b3a22dbf0ecb771e",nonce="723bb303214c479496ab83afc54eedc3",algorithm="MD5",
Content-Type: text/plain;charset=UTF-8
Content-Length: 33
Date: Sun, 09 Oct 2022 08:34:42 GMTHTTP/1.1 200 
Content-Type: text/plain;charset=UTF-8
Content-Length: 33
Date: Sun, 09 Oct 2022 08:34:42 GMTHello Spring Boot Demo.by digest.

在这里插入图片描述
接口打印日志,两次请求
在这里插入图片描述

[AuthController.java:73] =.= 测试浏览器原生弹窗登录认证-摘要认证-未登录
[AuthController.java:88] =.= WWW-Authenticate: Digest realm="realm",qop="auth,auth-int",opaque="0f7db485912f42d5b3a22dbf0ecb771e",nonce="723bb303214c479496ab83afc54eedc3",algorithm="MD5",
[AuthController.java:91] =.= 测试浏览器原生弹窗登录认证-摘要认证-验证登录
[AuthController.java:92] =.= auth, Digest username="asd", realm="realm", nonce="723bb303214c479496ab83afc54eedc3", uri="/auth/digest", cnonce="ICAgICAgICAgICAgICAgICAgICAgICAgIDIxMDU1MTU=", nc=00000001, qop=auth, response="21d1f834c6c12b9baf6f87ffdaf76edf", opaque="0f7db485912f42d5b3a22dbf0ecb771e", algorithm="MD5"
[AuthController.java:96] =.= auth-ap, Digest username="asd"
[AuthController.java:96] =.= auth-ap, realm="realm"
[AuthController.java:96] =.= auth-ap, nonce="723bb303214c479496ab83afc54eedc3"
[AuthController.java:96] =.= auth-ap, uri="/auth/digest"
[AuthController.java:96] =.= auth-ap, cnonce="ICAgICAgICAgICAgICAgICAgICAgICAgIDIxMDU1MTU="
[AuthController.java:96] =.= auth-ap, nc=00000001
[AuthController.java:96] =.= auth-ap, qop=auth
[AuthController.java:96] =.= auth-ap, response="21d1f834c6c12b9baf6f87ffdaf76edf"
[AuthController.java:96] =.= auth-ap, opaque="0f7db485912f42d5b3a22dbf0ecb771e"
[AuthController.java:96] =.= auth-ap, algorithm="MD5"
[AuthController.java:113] =.= MD5 name:realm:pass > asd:realm:123456 > 41fd06d7b3a49c6fc0d44d338b3b2fb8
[AuthController.java:123] =.=  method:uri > get:/hello/digest > a422e85fbdc5fb6cb7d362a4207ff531
[AuthController.java:131] =.= response ha1:nonce:nc:cnonce:qop:ha2 > 41fd06d7b3a49c6fc0d44d338b3b2fb8:"723bb303214c479496ab83afc54eedc3":00000001:"ICAgICAgICAgICAgICAgICAgICAgICAgIDIxMDU1MTU=":auth:a422e85fbdc5fb6cb7d362a4207ff531 > 04b7f44c993d847534f97489c80f08b6

END

这篇关于浏览器-基本认证(Basic Authentication)-摘要认证(digest authentication)=spring boot实现demo的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/352153

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

基本知识点

1、c++的输入加上ios::sync_with_stdio(false);  等价于 c的输入,读取速度会加快(但是在字符串的题里面和容易出现问题) 2、lower_bound()和upper_bound() iterator lower_bound( const key_type &key ): 返回一个迭代器,指向键值>= key的第一个元素。 iterator upper_bou