springboot系列(十)springboot整合shiro实现登录认证
關(guān)于shiro的概念和知識本篇不做詳細(xì)介紹,但是shiro的概念還是需要做做功課的要不無法理解它的運(yùn)作原理就無法理解使用shiro;
本篇主要講解如何使用shiro實(shí)現(xiàn)登錄認(rèn)證,下篇講解使用shiro實(shí)現(xiàn)權(quán)限控制
要實(shí)現(xiàn)shiro和springboot的整合需要以下幾大步驟:
下面我們一步步的詳細(xì)介紹:
一、生成用戶表
CREATE TABLE `sys_user` (`user_id` bigint(20) NOT NULL AUTO_INCREMENT,`username` varchar(50) NOT NULL COMMENT '用戶名',`password` varchar(100) DEFAULT NULL COMMENT '密碼',`salt` varchar(20) DEFAULT NULL COMMENT '鹽',`email` varchar(100) DEFAULT NULL COMMENT '郵箱',`mobile` varchar(100) DEFAULT NULL COMMENT '手機(jī)號',`status` tinyint(4) DEFAULT NULL COMMENT '狀態(tài) 0:禁用 1:正常',`dept_id` bigint(20) DEFAULT NULL COMMENT '部門ID',`create_time` datetime DEFAULT NULL COMMENT '創(chuàng)建時(shí)間',PRIMARY KEY (`user_id`),UNIQUE KEY `username` (`username`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='系統(tǒng)用戶';二、引入shiro依賴
<!-- Apache shiro依賴 只需要引入本依賴 shiro-spring 會自動引入shiro-web和shiro-core依賴--> <dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.3.2</version> </dependency>三、添加shiro的配置文件(本篇使用的是@Configuration注解java類的方式,也可以使用xml的方式)
package com.chuhouqi.demo.shiro;import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.session.mgt.SessionManager; 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.apache.shiro.web.session.mgt.DefaultWebSessionManager; import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;import javax.servlet.Filter; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map;@Configuration public class ShiroConfig {@Bean("sessionManager")public SessionManager sessionManager(){DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();sessionManager.setSessionValidationSchedulerEnabled(true);sessionManager.setSessionIdUrlRewritingEnabled(false);//sessionManager.setSessionIdCookieEnabled(false);return sessionManager;}@Bean("securityManager")public SecurityManager securityManager(UserRealm userRealm, SessionManager sessionManager) {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(userRealm);securityManager.setSessionManager(sessionManager);return securityManager;}@Bean("shiroFilter")public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();shiroFilter.setSecurityManager(securityManager);shiroFilter.setLoginUrl("/login");shiroFilter.setSuccessUrl("/index");shiroFilter.setUnauthorizedUrl("/403");Map<String, String> filterMap = new LinkedHashMap<>();filterMap.put("/druid/**", "anon");filterMap.put("/api/**", "anon");filterMap.put("/login", "anon");filterMap.put("/registe", "anon");filterMap.put("/registe.html", "anon");filterMap.put("/**/*.css", "anon");filterMap.put("/**/*.js", "anon");// filterMap.put("/login.html", "anon");filterMap.put("/fonts/**", "anon");filterMap.put("/plugins/**", "anon");filterMap.put("/swagger/**", "anon");filterMap.put("/favicon.ico", "anon");filterMap.put("/captcha.jpg", "anon");filterMap.put("/", "anon");filterMap.put("/**", "authc");shiroFilter.setFilterChainDefinitionMap(filterMap);return shiroFilter;}@Bean("lifecycleBeanPostProcessor")public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {return new LifecycleBeanPostProcessor();}@Beanpublic DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();proxyCreator.setProxyTargetClass(true);return proxyCreator;}@Beanpublic AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();advisor.setSecurityManager(securityManager);return advisor;}}四、添加自定義的realm(實(shí)現(xiàn)認(rèn)證和授權(quán))
package com.chuhouqi.demo.shiro;import com.chuhouqi.demo.common.utils.ShiroUtil; import com.chuhouqi.demo.entity.User; import com.chuhouqi.demo.service.IUserService; import org.apache.shiro.authc.*; import org.apache.shiro.authc.credential.CredentialsMatcher; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.authz.AuthorizationInfo; 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; import org.springframework.stereotype.Component;@Component public class UserRealm extends AuthorizingRealm {@Autowiredprivate IUserService userService;@Override/*** 權(quán)限授權(quán)*/protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {return null;}@Override/*** 登錄認(rèn)證*/protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {//獲取用戶輸入的用戶名String username = (String) token.getPrincipal();//根據(jù)用戶名查詢用戶信息User user = userService.getUser(username);// 賬號不存在if (user == null) {throw new UnknownAccountException("賬號不存在");}// 賬號鎖定if (user.getStatus() == 0) {throw new LockedAccountException("賬號已被鎖定,請聯(lián)系管理員");}SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(),ByteSource.Util.bytes(user.getSalt()),getName());return info;}/*** 設(shè)置密碼比較器為HashedCredentialsMatcher* @param credentialsMatcher*/@Overridepublic void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {HashedCredentialsMatcher shaCredentialsMatcher = new HashedCredentialsMatcher();shaCredentialsMatcher.setHashAlgorithmName(ShiroUtil.hashAlgorithmName);shaCredentialsMatcher.setHashIterations(ShiroUtil.hashIterations);super.setCredentialsMatcher(shaCredentialsMatcher);}}五、登錄操作觸發(fā)驗(yàn)證
package com.chuhouqi.demo.controller;import com.chuhouqi.demo.common.utils.ShiroUtil; import com.chuhouqi.demo.entity.User; import com.chuhouqi.demo.service.IUserService; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.*; import org.apache.shiro.subject.Subject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping;@Controller public class LoginController {private static Logger logger = LoggerFactory.getLogger(LoginController.class);@Autowiredprivate IUserService userService;@GetMapping("/login")String login() {return "login";}@RequestMapping("/login")public String login(String username, String password, Model model){try {Subject subject = SecurityUtils.getSubject();UsernamePasswordToken token = new UsernamePasswordToken(username, password);subject.login(token);}catch (UnknownAccountException e) {logger.error(e.getMessage());model.addAttribute("msg",e.getMessage());return "login";}catch (IncorrectCredentialsException e) {logger.error(e.getMessage());model.addAttribute("msg","賬號或密碼不正確");return "login";}catch (LockedAccountException e) {logger.error(e.getMessage());model.addAttribute("msg","賬號已被鎖定,請聯(lián)系管理員");return "login";}catch (AuthenticationException e) {logger.error(e.getMessage());model.addAttribute("msg","賬戶驗(yàn)證失敗");return "login";}return "index";}@RequestMapping("/registe")public String registe(User user){userService.saveUser(user);return "ok";}@RequestMapping("/logout")public String logout(){ShiroUtil.logout();return "redirect:/login";}}驗(yàn)證:
啟動項(xiàng)目,然后隨便請求一個(gè)路徑都會被shiro配置的filter攔截器進(jìn)行攔截,如果請求的路徑需要權(quán)限認(rèn)證就會進(jìn)入shiro的認(rèn)證管理中,如果當(dāng)前用戶沒有登錄就會調(diào)整到登錄頁面;
六、細(xì)節(jié)處理:
上面的介紹只是給出了一個(gè)大概的流程,其中有很多細(xì)節(jié)還是要特比注意的要不會導(dǎo)致認(rèn)證失敗,下面我們看一下有哪些細(xì)節(jié)需要處理
1、用戶密碼加密處理
在數(shù)據(jù)庫中存儲的用戶密碼不應(yīng)該是123456這樣的密碼明文,被不法分子看到是很危險(xiǎn)的,所以數(shù)據(jù)庫中的密碼應(yīng)該是對密碼進(jìn)行加密后的密文,而且還要求這個(gè)加密算法是不可逆的,即由加密后的字符串不能反推回來原來的密碼,如果能反推回來那這個(gè)加密是沒有意義的。
現(xiàn)在常用的加密算法有:?MD5,SHA1
而且shiro提供了SimpleHash這個(gè)加密工具來實(shí)現(xiàn)密碼加密:
public final static String hashAlgorithmName = "SHA-256";//加密算法public final static int hashIterations = 16;//hash加密次數(shù)
public static String encrypt(String pwd,String salt){
String newPassword = new SimpleHash(hashAlgorithmName,pwd,salt,hashIterations).toHex();
return newPassword;
}
如果兩個(gè)人的密碼一樣,即存在數(shù)據(jù)表里中的兩個(gè)加密后的字符串一樣,然而我們希望即使兩個(gè)人的密碼一樣,加密后的兩個(gè)字符串也不一樣。即需要用到MD5鹽值加密。
鹽值需要唯一: 一般使用隨機(jī)字符串或 user id
這里提供一個(gè)工具:
String salt = RandomStringUtils.randomAlphanumeric(20);//使用隨機(jī)數(shù)函數(shù)生成salt2、配置shiro的密碼比較器
上面我們用加密算法實(shí)現(xiàn)了密碼的明文加密,現(xiàn)在數(shù)據(jù)庫中存儲的是密碼密文,用戶登錄時(shí)使用的密碼原文,如果不告訴shiro我們的密碼加密算法邏輯,shiro是使用默認(rèn)的比較器
進(jìn)行的簡單的密碼比較(即使用數(shù)據(jù)庫中的密碼密文和用戶登錄時(shí)輸入的密碼明文進(jìn)行比較),顯而易見這樣比較是不會成功的所以我們要告訴shiro 我們是使用的加密算法,
實(shí)現(xiàn)過程很簡單:在我們自定義的UserRealm中添加如下配置
/*** 設(shè)置密碼比較器為HashedCredentialsMatcher* @param credentialsMatcher*/@Overridepublic void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {HashedCredentialsMatcher shaCredentialsMatcher = new HashedCredentialsMatcher();shaCredentialsMatcher.setHashAlgorithmName(ShiroUtil.hashAlgorithmName);//這里就是我們進(jìn)行密碼加密的算法shaCredentialsMatcher.setHashIterations(ShiroUtil.hashIterations);//加密循環(huán)次數(shù)super.setCredentialsMatcher(shaCredentialsMatcher);}?
轉(zhuǎn)載于:https://www.cnblogs.com/keepruning/p/9305596.html
總結(jié)
以上是生活随笔為你收集整理的springboot系列(十)springboot整合shiro实现登录认证的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 转:6.1海量数据处理
- 下一篇: 使用pipenv代替virtualenv