文章目錄
-
- 前言
- 代碼部分
- Shiro架構的引入
- 下章預告
- 結語
前言
由于将使用者的賬号密碼明文存儲在資料庫中具有不安全性,比如容易洩露,使用者覺得不靠譜等,是以我們需要将使用者的密碼加密存儲在資料庫中。
- Hash加密
hash 算法(雜湊演算法、摘要算法)即把任意長度的輸入映射為固定長度的輸出,比如密碼 Evanniubi 變成五位的輸出kchpl,這種算法不可逆,且存在資訊損失,雖然随着時間推移,出現了字典法、彩虹表法等優化手段,但本質上想要破解還是靠窮舉與瞎蒙,而且對于複雜密碼來說,破解成本極高。
-
加鹽加密
加鹽,是提高 hash 算法的安全性的一個常用手段。其意義在于利用你的賬戶生成“鹽”,通過這個鹽生成密碼并用hash加密。
為什麼更安全?
- 黑客就算拿到資料庫中的密碼也無法使用。
- 即使使用者使用相同的密碼,由于各個使用者的鹽不同,是以也無法破解使用者的密碼。
代碼部分
我們首先要在user表中添加salt字段,存儲每個使用者生成的“鹽”。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnLzADO5EzNyAjMxEjMwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
在SpringBoot的Pojo檔案夾下的User同樣增加Salt字段以及get,set方法。
-
引入Service層
大型的項目中,一般不由控制層直接調用Mapper層,是以需要提供一個Service層,讓它擔當Controller層與Mapper層的媒介,接收Controller層的請求,處理Mapper層的資料。是以我們在項目檔案下建立Service檔案夾,建立UserService.java檔案,注解@Service,代表Service層,并在其中對UserMapper進行@autowired 。
@Service public class UserService { @Autowired private UserMapper userMapper; public boolean checkUser(User user){ User u = userMapper.selectUser(user); if(u == null){ return false;//如果賬号密碼輸入不正确,傳回false } return true; } public boolean isExist(String username){ User user = userMapper.isExist(username); if(user != null){ return true;//從資料庫可以查找出使用者 } return false; } }
-
使用者注冊
前面說過,每個使用者生成唯一的鹽,是以我們得保證使用者是唯一的,這樣就需要使用者注冊的時候驗證資料庫中是否存在該賬号。
<select id="isExist" resultType="com.demo.demo.Pojo.User"> select * from user where username = #{username} </select>
@PostMapping("/register") public String register(@RequestBody User user){ if(userService.isExist(user.getUsername())){ return "使用者名已存在"; } // 根據使用者名生成鹽 String salt = new SecureRandomNumberGenerator().nextBytes().toString(); // 根據生成的鹽和使用者輸入的密碼疊代2次生成新的密碼 String newPassword = new SimpleHash("md5", user.getPassword(), salt, 2).toString(); // 存儲使用者資訊,包括 salt 與 hash 後的密碼 user.setSalt(salt); user.setPassword(newPassword); userService.add(user); return "注冊成功"; }
- Shiro的Jar包引入
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.4.0</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>1.4.0</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</version> </dependency>
- 前端提供一個注冊的按鈕寫注冊函數
register(){ var params = { username:this.username, password:this.password } axios.post('http://localhost:9527/user/register',params).then(res => { consoel.log(res) }) }
-
驗證
當輸入已存在使用者名:
當輸入不同使用者名:【Springboot+Vue】做一個權限管理背景(五):引入shiro 可以看到,資料庫中存儲了使用者的密碼和鹽值都是加密方式的,大大提高了使用者資訊的安全性。【Springboot+Vue】做一個權限管理背景(五):引入shiro 以後使用者登入的過程:【Springboot+Vue】做一個權限管理背景(五):引入shiro - 前端明文送出賬号密碼
- 後端根據使用者名取出資料庫中對應的鹽值
- 根據取出的鹽值進行同樣hash加密兩次
- 比如加密後的結果與資料庫中的是否一緻
Shiro架構的引入
Shiro提供了一系列安全的加密驗證,使用者認證,使用者授權,使用者會話等方法,我們的一切驗證授權手段都是在調用Shiro的API,與之相類似的有Spring Security。
-
Shiro的三個概念
Subject:目前與Shiro互動的東西,比如前端發送登入請求到後端,那麼Subject就是送出的使用者類。
SecurityManager :管理全部的Subject。
Realms:将使用者資訊進行一系列處理傳遞給SecurityManage。
-
Realm的配置
我們說過,Realm是輸入的使用者資訊與SecurityManaga之間的橋梁,那麼我們需要在Realm中做什麼呢?比如對使用者輸入的使用者名變成鹽,對輸入的密碼進行加密等,操作完後才送出給SecurityManage。
我們建立Realm檔案夾下建立UserRealm.java檔案,在其中對使用者資訊進行處理。
public class UserRealm extends AuthorizingRealm {//所有自定義的Realm必須繼承AuthorizingRealm @Autowired private UserService userService; @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { return null;//對登入使用者的授權,現在我們先不寫 } //此部分處理登入 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { String username = token.getPrincipal().toString();//token由控制層傳入,擷取token中存儲的使用者名 User user = userService.getUserByName(username);//根據使用者名去資料庫查詢是否存在該使用者 if(user == null){ throw new UnknownAccountException();//使用者不存在抛出不存在異常交給控制層處理 } String password = user.getPassword(); String salt = user.getSalt(); //再次把salt轉成byte将整個認證交給SecurityManage SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username,password, ByteSource.Util.bytes(salt),getName()); return authenticationInfo; } }
-
Shiro配置
在Config檔案夾下建立ShiroConfig.java檔案進行Shiro配置
package com.demo.demo.Confg; import com.demo.demo.Realm.UserRealm; import org.apache.shiro.authc.AbstractAuthenticator; import org.apache.shiro.authc.credential.CredentialsMatcher; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; 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; import org.apache.shiro.mgt.SecurityManager; @Configuration public class ShiroConfig { @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(getUserRealm()); return securityManager; } @Bean public UserRealm getUserRealm() { UserRealm realm = new UserRealm(); realm.setCredentialsMatcher(hashedCredentialsMatcher()); return realm; } @Bean public CredentialsMatcher hashedCredentialsMatcher() { HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); hashedCredentialsMatcher.setHashAlgorithmName("md5"); hashedCredentialsMatcher.setHashIterations(2); return hashedCredentialsMatcher; } @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } }
- 登入控制層重寫
@PostMapping("/login") public String Login(@RequestBody User user){ Subject subject = SecurityUtils.getSubject(); //Shiro幫我們寫好了UsernamePasswordToken,隻要送出賬号密碼,後面的交給Realm,Realm交給SecurityManage UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(),user.getPassword()); //隻要一行代碼就能實作登入 try { subject.login(token); return "登入成功"; }catch (UnknownAccountException e){ //處理我們在Realm中抛出的異常 return "使用者不存在"; }catch (AuthenticationException e){ //當Shiro發現使用者的賬号密碼不比對時自動抛出這個異常 return "賬号或密碼錯誤"; } }
- 登入驗證一下 發現登入成功,說明我們的Shiro配置完成,已實作基本的登入。
【Springboot+Vue】做一個權限管理背景(五):引入shiro
下章預告
- 對登入的使用者給予授權憑證
- 登出功能
結語
有問題的可以在評論區留言哦,也可以提出意見或者建議。