SpringBoot 整合 Shiro 密碼登錄與郵件驗(yàn)證碼登錄功能(多 Realm 認(rèn)證)
導(dǎo)入依賴(pom.xml)
<!--整合Shiro安全框架--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</version> </dependency> <!--集成jwt實(shí)現(xiàn)token認(rèn)證--> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.2.0</version> </dependency>
在 SpringBoot 項(xiàng)目配置 config 包下創(chuàng)建 ShiroConfig 配置類
@Configuration
public class ShiroConfig {
/**
* ShiroFilterFactoryBean
* <p>
* anon:無(wú)需認(rèn)證就可以訪問(wèn)
* authc:必須認(rèn)證才能訪問(wèn)
* user:必須擁有 記住我 功能才能用
* perms:擁有對(duì)某個(gè)資源的權(quán)限能訪問(wèn)
* role:擁有某個(gè)角色權(quán)限能訪問(wèn)
*/
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
// 設(shè)置安全管理器
factoryBean.setSecurityManager(defaultWebSecurityManager);
// 添加shiro的內(nèi)置過(guò)濾器
Map<String, String> filterMap = new LinkedHashMap<>();
// 放行不需要權(quán)限認(rèn)證的接口
// 網(wǎng)站首頁(yè)
filterMap.put("/", "anon");
filterMap.put("/index", "anon");
filterMap.put("/index.html", "anon");
// 不驗(yàn)證跳轉(zhuǎn)接口
filterMap.put("/into/**", "anon");
// 需要權(quán)限認(rèn)證的接口
// 驗(yàn)證跳轉(zhuǎn)接口
filterMap.put("/verifyInto/**", "authc");
factoryBean.setFilterChainDefinitionMap(filterMap);
// 訪問(wèn)沒(méi)有授權(quán)的資源
factoryBean.setLoginUrl("redirect:/into/login");
// 設(shè)置無(wú)權(quán)限時(shí)跳轉(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();
}
/**
* 注入 郵箱驗(yàn)證登錄EmailRealm
*/
@Bean
@DependsOn("lifecycleBeanPostProcessor")
public UserEmailRealm userEmailRealm() {
return new UserEmailRealm();
}
/**
* 默認(rèn)安全管理器
*/
@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;
}
/**
* 認(rèn)證器 把我們的自定義驗(yàn)證加入到認(rèn)證器中
*/
@Bean
public AbstractAuthenticator abstractAuthenticator(UserPasswordRealm userPasswordRealm, UserEmailRealm userEmailRealm) {
// 自定義模塊化認(rèn)證器,用于解決多realm拋出異常問(wèn)題
//開(kāi)始沒(méi)用自定義異常問(wèn)題,發(fā)現(xiàn)不管是賬號(hào)密碼錯(cuò)誤還是什么錯(cuò)誤
//shiro只會(huì)拋出一個(gè)AuthenticationException異常
ModularRealmAuthenticator authenticator = new MyCustomModularRealmAuthenticator();
// 認(rèn)證策略:AtLeastOneSuccessfulStrategy(默認(rèn)),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注解 切點(diǎn)
*/
@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有效時(shí)間
*/
@Bean
public SimpleCookie rememberMeCookie() {
/*這個(gè)參數(shù)是cookie的名稱,對(duì)應(yīng)前端頁(yè)面的checkbox的name=remremberMe*/
SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
/*cookie的有效時(shí)間為30天,單位秒*/
simpleCookie.setMaxAge(259200);
return simpleCookie;
}
}
創(chuàng)建自定義驗(yàn)證器 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)建密碼登錄時(shí)驗(yàn)證授權(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;
}
/**
* 認(rèn)證
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("————密碼認(rèn)證————doGetAuthenticationInfo————");
UsernamePasswordToken userToken = (UsernamePasswordToken) token;
// 連接數(shù)據(jù)庫(kù) 查詢用戶數(shù)據(jù)
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("user_name", userToken.getUsername());
User user = userMapper.selectOne(wrapper);
// 驗(yàn)證用戶
if (user == null) {
throw new UnknownAccountException();
}
return new SimpleAuthenticationInfo("", user.getUserPassword(), "");
}
/**
* 用來(lái)判斷是否使用當(dāng)前的 realm
*
* @param var1 傳入的token
* @return true就使用,false就不使用
*/
@Override
public boolean supports(AuthenticationToken var1) {
return var1 instanceof UsernamePasswordToken;
}
}
創(chuàng)建郵件驗(yàn)證碼登錄時(shí)驗(yàn)證授權(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("————郵箱登錄認(rèn)證————doGetAuthenticationInfo————");
UserEmailToken userEmailToken = (UserEmailToken) token;
String userEmail = (String) userEmailToken.getPrincipal();
// 連接數(shù)據(jù)庫(kù) 查詢用戶數(shù)據(jù)
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("user_email", userEmail);
User user = userService.getOne(wrapper);
//因?yàn)闆](méi)有密碼,并且驗(yàn)證碼在之前就驗(yàn)證了
if (user == null) {
throw new UnknownAccountException();
}
return new SimpleAuthenticationInfo("", userEmail, "");
}
/**
* 用來(lái)判斷是否使用當(dāng)前的 realm
*
* @param var1 傳入的token
* @return true就使用,false就不使用
*/
@Override
public boolean supports(AuthenticationToken var1) {
return var1 instanceof UserEmailToken;
}
}
創(chuàng)建郵件驗(yàn)證碼登錄驗(yàn)證通過(guò)生成令牌的 UserEmailToken 類(密碼登錄時(shí)使用shiro默認(rèn)的 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) {
// 獲取當(dāng)前的用戶
Subject subject = SecurityUtils.getSubject();
// 對(duì)密碼進(jìn)行MD5鹽值加密
String md5Password = new MDPasswordUtil().getMDPasswordUtil(userName, userPassword);
// 封裝用戶的登錄數(shù)據(jù)
UsernamePasswordToken token = new UsernamePasswordToken(userName, md5Password);
//rememberme記住我
token.setRememberMe(true);
try {
// 登錄,驗(yàn)證,保存令牌
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", "用戶名錯(cuò)誤!請(qǐng)重新輸入。");
return "login";
} catch (IncorrectCredentialsException ice) {
model.addAttribute("pwError", "密碼錯(cuò)誤!請(qǐng)重新輸入。");
return "login";
}
}
控制層用戶郵件驗(yàn)證碼密碼登錄
// 用戶郵箱登錄
@PostMapping("/emailLogin")
public String emailLogin(@RequestParam("userEmail") String userEmail,
@RequestParam("emailCode") String emailCode,
HttpSession session, Model model) {
// 根據(jù)userEmail從session中取出發(fā)送的驗(yàn)證碼
String sendEmailCode = (String) session.getAttribute(userEmail);
// 比對(duì)驗(yàn)證碼
if (StringUtils.isNoneBlank(sendEmailCode) && sendEmailCode.equals(emailCode)) {
try {
UserEmailToken token = new UserEmailToken(userEmail);
//rememberme記住我
token.setRememberMe(true);
// 登錄,驗(yàn)證,保存令牌
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);
// 銷毀驗(yàn)證碼
session.removeAttribute(emailCode);
return "admin";
} catch (Exception e) {
model.addAttribute("error", "驗(yàn)證碼錯(cuò)誤!請(qǐng)重新輸入。");
return "login";
}
} else {
return "login";
}
}
SpringBoot 整合 Shiro 密碼登錄與郵件驗(yàn)證碼登錄(多 Realm 認(rèn)證)就可以了 (有點(diǎn)多,哈哈哈)
推薦大神:狂神說(shuō)Java
到此這篇關(guān)于SpringBoot 整合 Shiro 密碼登錄與郵件驗(yàn)證碼登錄(多 Realm 認(rèn)證)的文章就介紹到這了,更多相關(guān)SpringBoot 整合 Shiro登錄內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java?spring?boot實(shí)現(xiàn)批量刪除功能詳細(xì)示例
這篇文章主要給大家介紹了關(guān)于Java?spring?boot實(shí)現(xiàn)批量刪除功能的相關(guān)資料,文中通過(guò)代碼以及圖文將實(shí)現(xiàn)的方法介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2023-08-08
Mybatis 實(shí)現(xiàn)動(dòng)態(tài)組裝查詢條件,仿SQL模式
這篇文章主要介紹了Mybatis 實(shí)現(xiàn)動(dòng)態(tài)組裝查詢條件,仿SQL模式的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06
IDEA一致卡在build時(shí)間過(guò)長(zhǎng)問(wèn)題解決
有很多小伙伴在起項(xiàng)目的時(shí)候巨慢,特別影響開(kāi)發(fā)效率,本文主要介紹了IDEA一致卡在build時(shí)間過(guò)長(zhǎng)問(wèn)題解決,具有一定的參考價(jià)值,感興趣的可以了解一下2024-06-06
Java設(shè)計(jì)模式之創(chuàng)建者模式簡(jiǎn)介
這篇文章主要介紹了Java設(shè)計(jì)模式之創(chuàng)建者模式,需要的朋友可以參考下2014-07-07
spring啟動(dòng)錯(cuò)誤Singleton bean creation not al
本文主要介紹了spring啟動(dòng)錯(cuò)誤Singleton bean creation not allowed while the singletons of this factory are indestruction,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07
SpringBoot集成Redisson操作Redis的實(shí)現(xiàn)方法
Redisson是一個(gè)用于Java的Redis客戶端,它提供了在分布式環(huán)境下操作Redis數(shù)據(jù)庫(kù)的簡(jiǎn)單、高效的方式,本文主要介紹了SpringBoot集成Redisson操作Redis的實(shí)現(xiàn)方法,具有一定的參考價(jià)值,感興趣的可以了解一下2024-03-03
uploadify上傳及后臺(tái)文件合法性驗(yàn)證的代碼解析
這篇文章主要介紹了uploadify上傳及后臺(tái)文件合法性驗(yàn)證的代碼解析,整段代碼分為后臺(tái)上傳方法,文件合法性驗(yàn)證類,前端上傳js,非常不錯(cuò)具有參考借鑒價(jià)值,需要的朋友可以參考下2016-11-11

