常用登录加密之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文件中,以至少一个空白字符开头,后面又跟了非…...
多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度
一、引言:多云环境的技术复杂性本质 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时,基础设施的技术债呈现指数级积累。网络连接、身份认证、成本管理这三大核心挑战相互嵌套:跨云网络构建数据…...
springboot 百货中心供应链管理系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...
基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...
PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建
制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...
【HTML-16】深入理解HTML中的块元素与行内元素
HTML元素根据其显示特性可以分为两大类:块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...
【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)
1.获取 authorizationCode: 2.利用 authorizationCode 获取 accessToken:文档中心 3.获取手机:文档中心 4.获取昵称头像:文档中心 首先创建 request 若要获取手机号,scope必填 phone,permissions 必填 …...
重启Eureka集群中的节点,对已经注册的服务有什么影响
先看答案,如果正确地操作,重启Eureka集群中的节点,对已经注册的服务影响非常小,甚至可以做到无感知。 但如果操作不当,可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...
IP如何挑?2025年海外专线IP如何购买?
你花了时间和预算买了IP,结果IP质量不佳,项目效率低下不说,还可能带来莫名的网络问题,是不是太闹心了?尤其是在面对海外专线IP时,到底怎么才能买到适合自己的呢?所以,挑IP绝对是个技…...
【Redis】笔记|第8节|大厂高并发缓存架构实战与优化
缓存架构 代码结构 代码详情 功能点: 多级缓存,先查本地缓存,再查Redis,最后才查数据库热点数据重建逻辑使用分布式锁,二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...
