常用登录加密之Shiro与Spring Security的使用对比
Shiro与Spring Security都是主流的身份认证和权限控制安全框架,Shiro偏向于前后端不分离平台,而Spring Security更偏向于前后端分离平台。接下来简单列一下两种登录验证的执行流程和示例,了解实际运用中的登录执行流程,然后重点剖析一下密码验证的过程。其实,密码验证的本质就是比较用户输入的凭证(密码)和存储的凭证(加密后的密码)是否匹配 ,如果一致,则表示密码验证通过。
文章目录
- Spring Security的登录过程
- 1.导入依赖
- 2.UsernamePasswordAuthenticationToken
- 3.UserDetailsServiceImpl
- 4.账号密码验证说明(重点)
- 加密说明
- 加密特点:不需要提供盐、每次加密结果都不一样
- 加密示例
- Shiro的登录过程
- 1.导入依赖
- 2.UsernamePasswordToken
- 3.UserRealm
- 4.账号密码验证说明(重点)
- 加密说明
- 加密特点:需要提供盐、加密的时候需要传入登录用户名、加密结果都是一样的
- 加密示例
Spring Security的登录过程
1.导入依赖
<dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-core</artifactId></dependency>
2.UsernamePasswordAuthenticationToken
在登录方法中通过UsernamePasswordAuthenticationToken把用户名、密码信息封装起来,使用AuthenticationManager的authenticate方法并传入UsernamePasswordAuthenticationToken对象
@RestController
public class LoginController {@Resourceprivate AuthenticationManager authenticationManager;@RequestMapping("/login")public R login(String userName, String password){Authentication authentication = null;try {UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userName, password);AuthenticationContextHolder.setContext(authenticationToken);// 该方法会去调用UserDetailsServiceImpl.loadUserByUsernameauthentication = authenticationManager.authenticate(authenticationToken);} catch (Exception e) {e.printStackTrace();return R.error();}//登录认证成功,生成token返回LoginUser loginUser = (LoginUser) authentication.getPrincipal();String token = getToken(loginUser); return R.ok().setData(token);}private String getToken(LoginUser loginUser) {//TODO 按照自己的业务需求生成return null;}
}
上下文对象,用来存放身份认证信息
/*** 上下文对象,用来存放身份认证信息*/
public class AuthenticationContextHolder {private static final ThreadLocal<Authentication> contextHolder = new ThreadLocal<>();public static Authentication getContext() {return contextHolder.get();}public static void setContext(Authentication context) {contextHolder.set(context);}public static void clearContext() {contextHolder.remove();}
}
3.UserDetailsServiceImpl
①同时定义UserDetailsServiceImpl类实现UserDetailsService,并重写loadUserByUsername方法,上面步骤就会执行到此方法中;
②在loadUserByUsername方法中通过上下文传值AuthenticationContextHolder获取到登录的用户名和密码;
③进行账号、密码验证
@Service
public class UserDetailsServiceImpl implements UserDetailsService {@Autowiredprivate UserService userService;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {User user = userService.selectUserByUserName(username);boolean state = validate(user); //验证用户信息if (!state) {//账号、密码验证失败throw new UsernameNotFoundException("账号、密码验证失败");}//生成UserDetails信息返回UserDetails userDetails = new LoginUser();return userDetails;}private boolean validate(User user){Authentication usernamePasswordAuthenticationToken = AuthenticationContextHolder.getContext();String password = usernamePasswordAuthenticationToken.getCredentials().toString();//验证账号、密码return new BCryptPasswordEncoder().matches(password, user.getPassword());}
}
4.账号密码验证说明(重点)
加密说明
Spring Security主要使用BCryptPasswordEncoder进行加密的,BCryptPasswordEncoder用SHA-256+随机盐+密钥对密码进行加密,这种加密过程中不需要使用密钥,输入明文后由系统直接经过加密算法处理成密文,这种加密后的数据是无法被解密的,无法根据密文推算出明文。BCryptPasswordEncoder的加密过程中,同一个密码,由于盐是随机的,所以每次加密的结果都是不一样的,然后就是明文密码的hash值与数据库中存储密码hash值进行比较。
加密特点:不需要提供盐、每次加密结果都不一样
加密示例
public static void main(String[] args) {//初始化BCryptPasswordEncoderBCryptPasswordEncoder encoder = new BCryptPasswordEncoder();//原始密码String initPwd = "123456";String encode1 = encoder.encode(initPwd);System.out.println("1次加密结果:"+encode1);String encode2 = encoder.encode(initPwd);System.out.println("2次加密结果:"+encode2);String encode3 = encoder.encode(initPwd);System.out.println("3次加密结果:"+encode3);boolean b1 = encoder.matches(initPwd, encode1);System.out.println("1次加密认证:"+b1);boolean b2 = encoder.matches(initPwd, encode2);System.out.println("2次加密认证:"+b2);boolean b3 = encoder.matches(initPwd, encode3);System.out.println("3次加密认证:"+b3);}
运行结果:
1次加密结果:$2a$10$8NfgZSeUG8mCApNeJumsg.Z6k3SF1HrGPB0FPuLDlFy2jYF.uHYAm
2次加密结果:$2a$10$.PTFbG0qwMHiQLgA5SY/pOOcHCUP7gZQvQYAEv6RRaE0R3Wc9e.hW
3次加密结果:$2a$10$YFI4vgXpnYlcfsqpplqJ1OjL8JczZSqYWjL0jIkmggTpe7cSuaaNi
1次加密认证:true
2次加密认证:true
3次加密认证:true
Shiro的登录过程
1.导入依赖
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-crypto-hash</artifactId><version>1.6.0</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</artifactId><version>1.6.0</version></dependency>
2.UsernamePasswordToken
通过UsernamePasswordToken这个类会将用户登录信息封装起来,生成Token,然后通过SecurityUtils下的subject传入token执行登录方法
@RestController
public class LoginController {@RequestMapping("/login")public R login(String userName, String password){UsernamePasswordToken token = new UsernamePasswordToken(userName, password);Subject subject = SecurityUtils.getSubject();try {subject.login(token);//验证通过,登录成功return R.ok();} catch (AuthenticationException e) {e.printStackTrace();//用户或密码错误,验证失败return R.error();}}
}
3.UserRealm
①认证器会将Token分解开来,分成账号和密码,并通过Relam这个桥梁向数据库进行求证;
②然后自定义UserRealm类并继承AuthorizingRealm方法,重写doGetAuthenticationInfo方法中就可以从token中获取用户账号、密码信息;
③进行账号、密码验证
public class UserRealm extends AuthorizingRealm {@Autowiredprivate UserService userService;@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {//授权方法 TODOreturn null;}@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {//登录方法UsernamePasswordToken upToken = (UsernamePasswordToken) authenticationToken;String userName = upToken.getUsername();String password = new String(upToken.getPassword());//执行登录验证User user = null;try {//验证账号、密码user = validate(userName, password);} catch (Exception e) {throw new AuthenticationException(e.getMessage(), e);}return new SimpleAuthenticationInfo(user, password, getName());}private User validate(String userName, String password) throws AuthenticationException {User user = userService.selectUserByUserName(userName);if (user == null) {throw new AuthenticationException("用户不存在");}//输入密码加密String inputPwd = encryptPassword(userName, password, user.getSalt());//数据库密码String dbPwd = user.getPassword();if (!inputPwd.equals(dbPwd)) {throw new AuthenticationException("用户名、密码验证失败");}return user;}private static String encryptPassword(String userName, String password, String salt) {return new Md5Hash(userName + password + salt).toHex().toString();}
}
4.账号密码验证说明(重点)
加密说明
Shiro主要使用的是MD5进行加密的,底层就是传入登录名、密码、盐,使用MD5进行hash加密计算,得到一个密文字符串,通常会把盐和这个密文字符串存入到数据库中。下次用户登录的时候,传入登录名和密码,从数据库中获取到盐,MD5加密生成此时的加密密文,与数据库中存储的加密密文进行匹配。如果一致则验证通过,如果不一致,则验证不通过。
加密特点:需要提供盐、加密的时候需要传入登录用户名、加密结果都是一样的
加密示例
public static void main(String[] args) {//密文密码String dbPwd = "3d3e2e119996cedb7401025cced5c1b0";//用户名String userName = "admin";//盐String salt = "111111";//明文密码String inputPwd = "123456";String encryptPassword = encryptPassword(userName, inputPwd, salt);System.out.println("加密结果:"+encryptPassword);boolean b = dbPwd.equals(encryptPassword);System.out.println("认证结果:"+b);}public static String encryptPassword(String userName, String password, String salt) {return new Md5Hash(userName + password + salt).toHex().toString();}
运行结果:
加密结果:3d3e2e119996cedb7401025cced5c1b0
认证结果:true
相关文章:
常用登录加密之Shiro与Spring Security的使用对比
Shiro与Spring Security都是主流的身份认证和权限控制安全框架,Shiro偏向于前后端不分离平台,而Spring Security更偏向于前后端分离平台。接下来简单列一下两种登录验证的执行流程和示例,了解实际运用中的登录执行流程,然后重点剖…...
获取文件路径里的文件名(不包含扩展名)
“./abc/abc/llf.jpg” 写一个代码,让我获得“llf”这段字符串 import osfile_path "./abc/abc/llf.jpg" file_name os.path.splitext(os.path.basename(file_path))[0] print(file_name)在这个代码中,我们使用了os.path模块来处理文件路径…...

HiveSql语法优化二 :join算法
Hive拥有多种join算法,包括Common Join,Map Join,Bucket Map Join,Sort Merge Buckt Map Join等,下面对每种join算法做简要说明: Common Join Common Join是Hive中最稳定的join算法,其通过一个M…...

Leetcode—459.重复的子字符串【简单】
2023每日刷题(五十九) Leetcode—459.重复的子字符串 算法思想 巧解的算法思想 实现代码 从第一个位置开始到s.size()之前,看s字符串是否是ss的子串 class Solution { public:bool repeatedSubstringPattern(string s) {return (s s).fin…...

Mac安装Typora实现markdown自由
一、什么是markdown Markdown 是一种轻量级标记语言,创始人为约翰格鲁伯(John Gruber)。 它允许人们使用易读易写的纯文本格式编写文档,然后转换成有效的 XHTML(或者HTML)文档。这种语言吸收了很多在电子邮…...
前后端传参格式
前端发送 Serialize()方法 是指将一个抽象的JavaScript对象(数据结构)转换成字符串。这个字符串可以利用标准格式发送到服务器,被视为URL查询字符串或者POST数据,或者由于复杂的AJAX请求。这个方法使用的数据结构可以是JavaScri…...

【后端学前端】第三天 css动画 动态搜索框(定位、动态设置宽度)
1、学习信息 视频地址:css动画 动态搜索框(定位、动态设置宽度)_哔哩哔哩_bilibili 2、源码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>test3</title>…...
51.0/表单(详细版)
目录 51.1 输入元素 input 51.1.1 文本域 51.1.2 密码输入框 51.1.3 单选框 51.1.4 复选框 51.1.5 提交按钮 51.1.6 重置按钮 51.1.7 普通按钮 51.1.8 图片按钮 51.1.9 隐藏域 51.1.10 文件域 51.2 多行文本框 51.3 下拉列表框 51.4 表单的综合示例 表单是网页中…...

动态规划(Dynamic Programming)
动态规划(Dynamic Programming):是运筹学的一种最优化方法,只不过在计算机问题上应用比较多 DP常见步骤: 暴力递归/穷举记忆化搜索(傻缓存 递归),使用备忘录/ DP Table 来优化穷举过程严格表结…...

linux使用文件描述符0、1和2来处理输入和输出
文件描述符012 在Linux中,文件描述符0、1和2分别代表标准输入(stdin)、标准输出(stdout)和标准错误(stderr)。它们用于处理进程的输入和输出。 文件描述符0(stdin)&…...
how to write and run .ps1
use .txt filechange the suffix to .ps1 from .txt 3)how to run .ps1 3.1) PS D:> .\test.ps1 1 2 3 4 5 6 7 8 9 10 3.2) PS D:> tes then press tab key to compensate and complete the whole file name...
如何在PHP中处理跨域请求?
在 PHP 中处理跨域请求(CORS,Cross-Origin Resource Sharing),通常需要在服务器端设置相应的 HTTP 头,以允许来自其他域的请求。以下是一些处理跨域请求的方法: 设置 HTTP 头: 在服务器端&#…...

spring boot 配置多数据源 踩坑 BindingException: Invalid bound statement (not found)
在上一篇:《【已解决】Spring Boot多数据源的时候,mybatis报错提示:Invalid bound statement (not found)》 凯哥(凯哥Java) 已经接受了,在Spring Boot配置多数据源时候,因为自己马虎,导致的一个坑。下面&a…...

【产品】Axure的基本使用(二)
文章目录 一、元件基本介绍1.1 概述1.2 元件操作1.3 热区的使用 二、表单型元件的使用2.1 文本框2.2 文本域2.3 下拉列表2.4 列表框2.5 单选按钮2.6 复选框2.7 菜单与表格元件的使用 三、实例3.1 登录2.2 个人简历 一、元件基本介绍 1.1 概述 在Axure RP中,元件是…...

Python语言学习笔记之十(字符串处理)
本课程对于有其它语言基础的开发人员可以参考和学习,同时也是记录下来,为个人学习使用,文档中有此不当之处,请谅解。 字符串处理:以实现字符串的分割、替换、格式化、大小写转换,Python字符串处理是指对Py…...

WPF-附加属性《十二》
非常重要 依赖属性和附加属性,两者是有关系的,也是有些区别的,很多时候,可能会把两者混淆了。 附加属性(Attach Property) 顾名思义,就是附加上面的属性,自身是没有的,…...

算法通关第十九关-青铜挑战理解动态规划
大家好我是苏麟 , 今天聊聊动态规划 . 动态规划是最热门、最重要的算法思想之一,在面试中大量出现,而且题目整体都偏难一些对于大部人来说,最大的问题是不知道动态规划到底是怎么回事。很多人看教程等,都被里面的状态子问题、状态…...

2023 GitHub年度排行榜,JEECG上榜第三名,势头依然很猛~
2023 GitHub年度排行榜TOP10,JeecgBoot上榜第三名,势头依然很猛~...

由@EnableWebMvc注解引发的Jackson解析异常
同事合了代码到开发分支,并没有涉及到改动的类却报错。错误信息如下: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.http.conv…...

ce从初阶到大牛--函数
1、显示/etc/passwd文件中以bash结尾的行; grep "bash$" /etc/passwd2、找出/etc/passwd文件中的三位或四位数; grep -E \b[0-9]{3,4}\b /etc/passwd3、找出/etc/grub2.cfg文件中,以至少一个空白字符开头,后面又跟了非…...

Flask RESTful 示例
目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题: 下面创建一个简单的Flask RESTful API示例。首先,我们需要创建环境,安装必要的依赖,然后…...
反向工程与模型迁移:打造未来商品详情API的可持续创新体系
在电商行业蓬勃发展的当下,商品详情API作为连接电商平台与开发者、商家及用户的关键纽带,其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息(如名称、价格、库存等)的获取与展示,已难以满足市场对个性化、智能…...

Docker 运行 Kafka 带 SASL 认证教程
Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明:server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...
如何为服务器生成TLS证书
TLS(Transport Layer Security)证书是确保网络通信安全的重要手段,它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书,可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...
3403. 从盒子中找出字典序最大的字符串 I
3403. 从盒子中找出字典序最大的字符串 I 题目链接:3403. 从盒子中找出字典序最大的字符串 I 代码如下: class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...

ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...
ip子接口配置及删除
配置永久生效的子接口,2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...

中医有效性探讨
文章目录 西医是如何发展到以生物化学为药理基础的现代医学?传统医学奠基期(远古 - 17 世纪)近代医学转型期(17 世纪 - 19 世纪末)现代医学成熟期(20世纪至今) 中医的源远流长和一脉相承远古至…...

uniapp手机号一键登录保姆级教程(包含前端和后端)
目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号(第三种)后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...
CSS | transition 和 transform的用处和区别
省流总结: transform用于变换/变形,transition是动画控制器 transform 用来对元素进行变形,常见的操作如下,它是立即生效的样式变形属性。 旋转 rotate(角度deg)、平移 translateX(像素px)、缩放 scale(倍数)、倾斜 skewX(角度…...