上篇的文章使用Shiro实现用户信息加密,小伙伴们都看了吗?没有看的点击进入查看Spring Boot + Vue先后端分离(九)使用Shiro实现用户信息加密。已经学习的,搬起小板凳 咱们继续下一个知识点的学习。php
本文是Spring Boot + Vue先后端分离 系列的第十篇,了解前面的文章有助于更好的理解本文:html
1.Spring Boot + Vue先后端分离(一)前端Vue环境搭建
2.Spring Boot + Vue先后端分离(二)前端Vue启动流程
3.Spring Boot + Vue先后端分离(三)实现登陆功能
4.Spring Boot + Vue先后端分离(四)前端路由
5.Spring Boot + Vue先后端分离(五)登陆拦截器
6.Spring Boot + Vue先后端分离(六)使用Element渲染登陆界面
7.Spring Boot + Vue先后端分离(七)后端系统,功能导航页
8.Spring Boot + Vue先后端分离(八)权限数据库设计
9.Spring Boot + Vue先后端分离(九)使用Shiro实现用户信息加密前端
目录git
(一).登陆验证--继承AuthorizingRealmgithub
(二).Shiro配置web
(三).登陆验证(登陆控制器)算法
前言spring
关于上一篇的文章原本写的时候是将用户信息加密和登陆认证在一块儿写的,可是在写文章的时候发现一篇太长,惧怕有的小伙伴看起来费劲,反而分开学习比较清晰,两个也能够单独分开来写。数据库
(一).登陆验证--继承AuthorizingRealm
apache
Shiro中有三个核心概念:Subject、SecurityManager 和 Realms。
Subject:“如今在与软件交互的东西”,其实就是一个用户类,负责存储与修改当前用户的信息和状态。
以后你会看到,使用 Shiro 实现咱们所设计的各类功能,实际上就是在调用 Subject。
SecurityManager:对安全相关的操做进行管理。只用在项目中配置一次。
Realm:是 Shiro 和安全相关数据(好比用户信息)的桥梁,也就是说,Realm 负责从数据源中获取数据并加工后传给 SecurityManager。
咱们能够经过配置使用特定的 Realm 替代 DAO,和 JPA 相似,Realm 获取数据的方法被封装了起来,可是数据库中的表名、字段等须要与源码预约义的查询保持一致,因此在咱们的项目中获取数据的功能仍旧能够交给 JPA 完成,Realm 只负责加工并传递这些数据。
另外还有其余一些概念,都须要了解一下——Authentication(认证)、Authorization(受权)、Session Management(会话管理)、Cryptography(加密),各类安全框架解决的都是这几类问题。
下面咱们说说怎么实现登陆认证?
1,咱们先建立一个类 BookCricleRealm 继承 AuthorizingRealm
package com.cxzc.mycxzc.demo.config; import com.cxzc.mycxzc.demo.bean.User;import com.cxzc.mycxzc.demo.service.UserService;import org.apache.shiro.authc.AuthenticationException;import org.apache.shiro.authc.AuthenticationInfo;import org.apache.shiro.authc.AuthenticationToken;import org.apache.shiro.authc.SimpleAuthenticationInfo;import org.apache.shiro.authz.AuthorizationInfo;import org.apache.shiro.authz.SimpleAuthorizationInfo;import org.apache.shiro.realm.AuthorizingRealm;import org.apache.shiro.subject.PrincipalCollection;import org.apache.shiro.util.ByteSource;import org.springframework.beans.factory.annotation.Autowired; public class BookCricleRealm extends AuthorizingRealm { @Autowired private UserService userService; /** * 获取用户角色和权限,用于权限认证 * @param principalCollection * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { SimpleAuthorizationInfo s = new SimpleAuthorizationInfo(); return s; } /** * 获取认证信息,即根据 token 中的用户名从数据库中获取密码、盐等并返回 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { //获取输入的用户帐号,并经过帐号获取相关信息 String userName = token.getPrincipal().toString(); User user = userService.getByName(userName); String passwordDB = user.getPassword(); String salt = user.getSalt(); //将查询到的用户帐号和密码存放到 authenticationInfo用于后面的权限判断。第三个参数传入用户输入的用户名。 SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(userName, passwordDB, getName()); System.out.println("user=="+user.toString()); //设置盐,用来核对密码 authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes(salt)); return authenticationInfo; }}
解释:
1,重写了两个权限认证函数
(1)protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { (2)protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
咱们作登陆 使用第二个,第一个后面作角色权限的时候使用
2,String userName = token.getPrincipal().toString();
User user = userService.getByName(userName);
//获取输入的用户帐号,并经过帐号获取相关信息
3,
//将查询到的用户帐号和密码存放到 authenticationInfo用于后面的权限判断。第三个参数传入用户输入的用户名。 SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(userName, passwordDB, getName());
4,
//设置盐,用来核对密码
authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes(salt));
(二).Shiro配置
关于Shiro配置咱们新建一个类ShiroConfiguration ,添加以下几个函数:
package com.cxzc.mycxzc.demo.config;import org.apache.shiro.authc.credential.HashedCredentialsMatcher;import org.apache.shiro.mgt.SecurityManager;import org.apache.shiro.spring.LifecycleBeanPostProcessor;import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;import org.apache.shiro.spring.web.ShiroFilterFactoryBean;import org.apache.shiro.web.mgt.DefaultWebSecurityManager;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration; /** * 公众号:程序职场 * Auther:chenjianpeng */@Configurationpublic class ShiroConfiguration { @Bean public static LifecycleBeanPostProcessor getLifecycleBeanProcessor() { return new LifecycleBeanPostProcessor(); } @Bean public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); return shiroFilterFactoryBean; } @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(getWJRealm()); return securityManager; } @Bean public BookCricleRealm getWJRealm() { BookCricleRealm wjRealm = new BookCricleRealm(); wjRealm.setCredentialsMatcher(hashedCredentialsMatcher()); return wjRealm; } @Bean public HashedCredentialsMatcher hashedCredentialsMatcher() { HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); hashedCredentialsMatcher.setHashAlgorithmName("md5"); hashedCredentialsMatcher.setHashIterations(3); return hashedCredentialsMatcher; } @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; }}
解释:
1,hashedCredentialsMatcher 类中添加设置
hashedCredentialsMatcher.setHashAlgorithmName("md5"); hashedCredentialsMatcher.setHashIterations(3);
(三).登陆验证(登陆控制器)
上面咱们把 Shiro的认证和配置都添加了,下面咱们说一下 登陆认证 在控制器中怎么实现的。若是上面有疑问的小伙伴加我微信,畅聊。。。。
@CrossOrigin @PostMapping(value = "login") public Result login(@RequestBody User requestUser) { String username = requestUser.getUsername(); username = HtmlUtils.htmlEscape(username); Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username,requestUser.getPassword()); try { subject.login(usernamePasswordToken); return ResultFactory.buildSucce***esult(usernamePasswordToken); } catch (AuthenticationException e) { String message = "密码错误"; return ResultFactory.buildFailResult(message); } }
解释:
1,这里的表面操做很是简单,主要是subject.login(usernamePasswordToken); 其实里面走了不少操做,这个是你没法想象的,Shiro 经过 Realm 里咱们重写的 doGetAuthenticationInfo
方法获取到了验证信息,再根据咱们在配置类里定义的 CredentialsMatcher(HashedCredentialsMatcher),执行以下方法:
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) { Object tokenHashedCredentials = this.hashProvidedCredentials(token, info); Object accountCredentials = this.getCredentials(info); return this.equals(tokenHashedCredentials, accountCredentials);}
其中accountCredentials 获取到了咱们存在数据库中的 hash 后的密码, 而 tokenHashedCredentials 则调用 hash 算法根据 salt 和客户端传入的 password 算出了 hash 值。
写完这篇文章 感受挺轻松的,写以前其实想的挺好的,很快就能更新完,为啥这么说呢?首先写这个项目的文章准备的充分,在开始以前都梳理了一下怎么写对你们有用?,写什么对你们有用?,其次 在时间上也耗费了不少。可是真的开始写的时候其实很累的。
但愿后面愈来愈顺畅。
源码连接:https://github.com/ProceduralZC/bookcircle
敬请期待下篇。。。