上篇的文章使用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
敬請期待下篇。。。