SpringBoot 整合 Shiro 密碼登錄與郵件驗證碼登錄功能(多 Realm 認證)
導(dǎo)入依賴(pom.xml)
<!--整合Shiro安全框架--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</version> </dependency> <!--集成jwt實現(xiàn)token認證--> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.2.0</version> </dependency>
在 SpringBoot 項目配置 config 包下創(chuàng)建 ShiroConfig 配置類
@Configuration public class ShiroConfig { /** * ShiroFilterFactoryBean * <p> * anon:無需認證就可以訪問 * authc:必須認證才能訪問 * user:必須擁有 記住我 功能才能用 * perms:擁有對某個資源的權(quán)限能訪問 * role:擁有某個角色權(quán)限能訪問 */ @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) { ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean(); // 設(shè)置安全管理器 factoryBean.setSecurityManager(defaultWebSecurityManager); // 添加shiro的內(nèi)置過濾器 Map<String, String> filterMap = new LinkedHashMap<>(); // 放行不需要權(quán)限認證的接口 // 網(wǎng)站首頁 filterMap.put("/", "anon"); filterMap.put("/index", "anon"); filterMap.put("/index.html", "anon"); // 不驗證跳轉(zhuǎn)接口 filterMap.put("/into/**", "anon"); // 需要權(quán)限認證的接口 // 驗證跳轉(zhuǎn)接口 filterMap.put("/verifyInto/**", "authc"); factoryBean.setFilterChainDefinitionMap(filterMap); // 訪問沒有授權(quán)的資源 factoryBean.setLoginUrl("redirect:/into/login"); // 設(shè)置無權(quán)限時跳轉(zhuǎn)的url factoryBean.setUnauthorizedUrl("redirect:/into/login"); return factoryBean; } /** * 管理shiro的生命周期 */ @Bean("lifecycleBeanPostProcessor") public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } /** * 注入 密碼登錄CustomRealm */ @Bean @DependsOn("lifecycleBeanPostProcessor") public UserPasswordRealm userPasswordRealm() { return new UserPasswordRealm(); } /** * 注入 郵箱驗證登錄EmailRealm */ @Bean @DependsOn("lifecycleBeanPostProcessor") public UserEmailRealm userEmailRealm() { return new UserEmailRealm(); } /** * 默認安全管理器 */ @Bean public DefaultWebSecurityManager securityManager(UserPasswordRealm userPasswordRealm, UserEmailRealm userEmailRealm, AbstractAuthenticator abstractAuthenticator) { DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager(); List<Realm> realms = new ArrayList<>(); realms.add(userPasswordRealm); realms.add(userEmailRealm); defaultWebSecurityManager.setRealms(realms); // 記住我 defaultWebSecurityManager.setRememberMeManager(cookieRememberMeManager()); defaultWebSecurityManager.setAuthenticator(abstractAuthenticator); return defaultWebSecurityManager; } /** * 認證器 把我們的自定義驗證加入到認證器中 */ @Bean public AbstractAuthenticator abstractAuthenticator(UserPasswordRealm userPasswordRealm, UserEmailRealm userEmailRealm) { // 自定義模塊化認證器,用于解決多realm拋出異常問題 //開始沒用自定義異常問題,發(fā)現(xiàn)不管是賬號密碼錯誤還是什么錯誤 //shiro只會拋出一個AuthenticationException異常 ModularRealmAuthenticator authenticator = new MyCustomModularRealmAuthenticator(); // 認證策略:AtLeastOneSuccessfulStrategy(默認),AllSuccessfulStrategy,F(xiàn)irstSuccessfulStrategy authenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy()); // 加入realms List<Realm> realms = new ArrayList<>(); realms.add(userPasswordRealm); realms.add(userEmailRealm); authenticator.setRealms(realms); return authenticator; } /** * 加入shiro注解 代理生成器 切面 */ @Bean @DependsOn({"lifecycleBeanPostProcessor"}) public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); advisorAutoProxyCreator.setProxyTargetClass(true); return advisorAutoProxyCreator; } /** * 加入shiro注解 切點 */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } /** * 設(shè)置cookie 記住我生成cookie */ @Bean public CookieRememberMeManager cookieRememberMeManager() { CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager(); cookieRememberMeManager.setCookie(rememberMeCookie()); return cookieRememberMeManager; } /** * 設(shè)置cookie有效時間 */ @Bean public SimpleCookie rememberMeCookie() { /*這個參數(shù)是cookie的名稱,對應(yīng)前端頁面的checkbox的name=remremberMe*/ SimpleCookie simpleCookie = new SimpleCookie("rememberMe"); /*cookie的有效時間為30天,單位秒*/ simpleCookie.setMaxAge(259200); return simpleCookie; } }
創(chuàng)建自定義驗證器 MyCustomModularRealmAuthenticator 類
public class MyCustomModularRealmAuthenticator extends ModularRealmAuthenticator { @Override protected AuthenticationInfo doMultiRealmAuthentication(Collection<Realm> realms, AuthenticationToken token) { AuthenticationStrategy authenticationStrategy = this.getAuthenticationStrategy(); AuthenticationInfo authenticationInfo = authenticationStrategy.beforeAllAttempts(realms, token); Iterator var5 = realms.iterator(); while (var5.hasNext()) { Realm realm = (Realm) var5.next(); authenticationInfo = authenticationStrategy.beforeAttempt(realm, token, authenticationInfo); if (realm.supports(token)) { AuthenticationInfo info = null; Throwable t = null; info = realm.getAuthenticationInfo(token); authenticationInfo = authenticationStrategy.afterAttempt(realm, token, info, authenticationInfo, t); } } authenticationInfo = authenticationStrategy.afterAllAttempts(token, authenticationInfo); return authenticationInfo; } }
創(chuàng)建密碼登錄時驗證授權(quán) UserPasswordRealm 類
@Component public class UserPasswordRealm extends AuthorizingRealm { // 注入用戶業(yè)務(wù) @Autowired private UserMapper userMapper; /** * 授權(quán) */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { System.out.println("————密碼授權(quán)————doGetAuthorizationInfo————"); return null; } /** * 認證 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("————密碼認證————doGetAuthenticationInfo————"); UsernamePasswordToken userToken = (UsernamePasswordToken) token; // 連接數(shù)據(jù)庫 查詢用戶數(shù)據(jù) QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.eq("user_name", userToken.getUsername()); User user = userMapper.selectOne(wrapper); // 驗證用戶 if (user == null) { throw new UnknownAccountException(); } return new SimpleAuthenticationInfo("", user.getUserPassword(), ""); } /** * 用來判斷是否使用當前的 realm * * @param var1 傳入的token * @return true就使用,false就不使用 */ @Override public boolean supports(AuthenticationToken var1) { return var1 instanceof UsernamePasswordToken; } }
創(chuàng)建郵件驗證碼登錄時驗證授權(quán)UserEmailRealm 類
@Component public class UserEmailRealm extends AuthorizingRealm { // 注入用戶業(yè)務(wù) @Autowired UserService userService; @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println("————郵箱登錄授權(quán)————doGetAuthorizationInfo————"); return null; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("————郵箱登錄認證————doGetAuthenticationInfo————"); UserEmailToken userEmailToken = (UserEmailToken) token; String userEmail = (String) userEmailToken.getPrincipal(); // 連接數(shù)據(jù)庫 查詢用戶數(shù)據(jù) QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.eq("user_email", userEmail); User user = userService.getOne(wrapper); //因為沒有密碼,并且驗證碼在之前就驗證了 if (user == null) { throw new UnknownAccountException(); } return new SimpleAuthenticationInfo("", userEmail, ""); } /** * 用來判斷是否使用當前的 realm * * @param var1 傳入的token * @return true就使用,false就不使用 */ @Override public boolean supports(AuthenticationToken var1) { return var1 instanceof UserEmailToken; } }
創(chuàng)建郵件驗證碼登錄驗證通過生成令牌的 UserEmailToken 類(密碼登錄時使用shiro默認的 UsernamePasswordToken 令牌)
@Data // 使用lombok 生成get方法、set方法 public class UserEmailToken implements HostAuthenticationToken, RememberMeAuthenticationToken { private String userEmail; private boolean rememberMe; private String host; public UserEmailToken() { this.rememberMe = false; } public UserEmailToken(String userEmail) { this(userEmail, false, null); } public UserEmailToken(String userEmail, boolean rememberMe) { this(userEmail, rememberMe, null); } public UserEmailToken(String userEmail, boolean rememberMe, String host) { this.userEmail = userEmail; this.rememberMe = rememberMe; this.host = host; } @Override public String getHost() { return host; } @Override public boolean isRememberMe() { return rememberMe; } /** * 重寫getPrincipal方法 */ @Override public Object getPrincipal() { return userEmail; } /** * 重寫getCredentials方法 */ @Override public Object getCredentials() { return userEmail; } }
創(chuàng)建密碼鹽值加密 MDPasswordUtil 工具類
public class MDPasswordUtil { public String getMDPasswordUtil(String userName, String userPassword) { String hashAlgorithmName = "MD5"; // 加密方式:md5加密 Object credentials = userPassword; // 密碼 Object salt = ByteSource.Util.bytes(userName); // 鹽 int hashIterations = 512; // 加密次數(shù) Object result = new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations); return result.toString(); } }
控制層用戶密碼登錄
// 用戶密碼登錄 @PostMapping("/passwordLogin") public String userLogin(@RequestParam("userName") String userName, @RequestParam("userPassword") String userPassword, HttpSession session, Model model) { // 獲取當前的用戶 Subject subject = SecurityUtils.getSubject(); // 對密碼進行MD5鹽值加密 String md5Password = new MDPasswordUtil().getMDPasswordUtil(userName, userPassword); // 封裝用戶的登錄數(shù)據(jù) UsernamePasswordToken token = new UsernamePasswordToken(userName, md5Password); //rememberme記住我 token.setRememberMe(true); try { // 登錄,驗證,保存令牌 subject.login(token); //查詢登錄信息 QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.eq("user_name", userName); User user = userService.getOne(wrapper); //保存登錄用戶信息 session.setAttribute(user.getUserId().toString(), user); return "admin"; } catch (UnknownAccountException e) { model.addAttribute("userError", "用戶名錯誤!請重新輸入。"); return "login"; } catch (IncorrectCredentialsException ice) { model.addAttribute("pwError", "密碼錯誤!請重新輸入。"); return "login"; } }
控制層用戶郵件驗證碼密碼登錄
// 用戶郵箱登錄 @PostMapping("/emailLogin") public String emailLogin(@RequestParam("userEmail") String userEmail, @RequestParam("emailCode") String emailCode, HttpSession session, Model model) { // 根據(jù)userEmail從session中取出發(fā)送的驗證碼 String sendEmailCode = (String) session.getAttribute(userEmail); // 比對驗證碼 if (StringUtils.isNoneBlank(sendEmailCode) && sendEmailCode.equals(emailCode)) { try { UserEmailToken token = new UserEmailToken(userEmail); //rememberme記住我 token.setRememberMe(true); // 登錄,驗證,保存令牌 Subject subject = SecurityUtils.getSubject(); subject.login(token); //查詢登錄信息 QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.eq("user_email", userEmail); User user = userService.getOne(wrapper); //保存登錄用戶信息 session.setAttribute(user.getUserId().toString(), user); // 銷毀驗證碼 session.removeAttribute(emailCode); return "admin"; } catch (Exception e) { model.addAttribute("error", "驗證碼錯誤!請重新輸入。"); return "login"; } } else { return "login"; } }
SpringBoot 整合 Shiro 密碼登錄與郵件驗證碼登錄(多 Realm 認證)就可以了 (有點多,哈哈哈)
推薦大神:狂神說Java
到此這篇關(guān)于SpringBoot 整合 Shiro 密碼登錄與郵件驗證碼登錄(多 Realm 認證)的文章就介紹到這了,更多相關(guān)SpringBoot 整合 Shiro登錄內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java?spring?boot實現(xiàn)批量刪除功能詳細示例
這篇文章主要給大家介紹了關(guān)于Java?spring?boot實現(xiàn)批量刪除功能的相關(guān)資料,文中通過代碼以及圖文將實現(xiàn)的方法介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2023-08-08Mybatis 實現(xiàn)動態(tài)組裝查詢條件,仿SQL模式
這篇文章主要介紹了Mybatis 實現(xiàn)動態(tài)組裝查詢條件,仿SQL模式的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06spring啟動錯誤Singleton bean creation not al
本文主要介紹了spring啟動錯誤Singleton bean creation not allowed while the singletons of this factory are indestruction,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07SpringBoot集成Redisson操作Redis的實現(xiàn)方法
Redisson是一個用于Java的Redis客戶端,它提供了在分布式環(huán)境下操作Redis數(shù)據(jù)庫的簡單、高效的方式,本文主要介紹了SpringBoot集成Redisson操作Redis的實現(xiàn)方法,具有一定的參考價值,感興趣的可以了解一下2024-03-03