歷史文章
(一)Spring Boot 集成 Shiro 權(quán)限管理與密碼加鹽
(三)Spring Boot 集成 Shiro 權(quán)限緩存功能
(四)Spring Boot 集成 Shiro 用戶管理與登錄保護(hù)
主要完成功能
用戶可以使用賬號(hào)密碼登錄或者手機(jī)驗(yàn)證碼登錄
shiro 有三種認(rèn)證策略 AuthenticationStrategy
-
AtLeastOneSuccessfulStrategy只要有一個(gè)成功即成功認(rèn)證 -
FirstSuccessfulStrategy第一個(gè)認(rèn)證成功即成功認(rèn)證 -
AllSuccessfulStrategy全部認(rèn)證成功即成功認(rèn)證否則失敗
默認(rèn)是 AtLeastOneSuccessfulStrategy
修改 MyRealm 為 BaseRealm 并將 doGetAuthenticationInfo 交由子類實(shí)現(xiàn)
實(shí)現(xiàn)兩個(gè) Realm
- PasswordRealm
- PhoneCodeRealm
public class PasswordRealm extends BaseRealm {
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//獲取用戶的輸入的賬號(hào)
UserToken userToken = (UserToken) authenticationToken;
if (userToken.getLoginType() == LoginType.PASSWORD) {
//通過(guò)username從數(shù)據(jù)庫(kù)中查找 User對(duì)象
User user = userMapper.selectByName(userToken.getUsername());
if (user == null) {
//沒(méi)有返回登錄用戶名對(duì)應(yīng)的SimpleAuthenticationInfo對(duì)象時(shí),就會(huì)在LoginController中拋出UnknownAccountException異常
throw new UnknownAccountException("用戶不存在");
}
//使用密碼加鹽的方式驗(yàn)證密碼的安全,鹽為用戶注冊(cè)時(shí)設(shè)置的用戶名
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
user, //用戶名
user.getPassword(), //密碼
ByteSource.Util.bytes(user.getSalt()),//salt
getName() //realm name
);
return authenticationInfo;
} else {
return null;
}
}
}
public class PhoneCodeRealm extends BaseRealm {
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UserToken userToken = (UserToken) authenticationToken;
if (userToken.getLoginType() == LoginType.PHONE_CODE) {
User user = userMapper.selectByName(userToken.getUsername());
if (user == null) {
throw new UnknownAccountException("用戶不存在");
}
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
user, //用戶名
"1234", //驗(yàn)證碼,該驗(yàn)證碼應(yīng)該從數(shù)據(jù)庫(kù)中查找
getName() //realm name
);
return authenticationInfo;
} else {
return null;
}
}
}
public enum LoginType {
PASSWORD, PHONE_CODE;
}
public class UserToken extends UsernamePasswordToken {
private LoginType loginType;
public UserToken(String username, String password, boolean rememberMe, LoginType loginType) {
super(username, password, rememberMe);
this.loginType = loginType;
}
public LoginType getLoginType() {
return loginType;
}
public void setLoginType(LoginType loginType) {
this.loginType = loginType;
}
}
修改 ShiroConfig 配置類
由之前的配置的單個(gè) Realm 改為 Realm List
@Bean
SecurityManager securityManager() {
DefaultSecurityManager defaultSecurityManager = new DefaultWebSecurityManager();
// >>>>修改為 List
List<Realm> realmList = new ArrayList<>();
realmList.add(passwordRealm());
realmList.add(phoneRealm());
defaultSecurityManager.setRealms(realmList);
// <<<<
defaultSecurityManager.setRememberMeManager(rememberMeManager());
defaultSecurityManager.setCacheManager(cacheManager());
defaultSecurityManager.setSessionManager(sessionManager());
return defaultSecurityManager;
}
@Bean
BaseRealm passwordRealm() {
BaseRealm myRealm = new PasswordRealm();
myRealm.setCredentialsMatcher(hashedCredentialsMatcher()); //設(shè)置解密規(guī)則
return myRealm;
}
@Bean
BaseRealm phoneRealm() {
return new PhoneCodeRealm();//去除解密策略
}
修改登錄接口
/**
* 登錄接口
*
* @param username
* @param password
*/
@GetMapping("/doLogin")
public String doLogin(String username, String password, boolean rememberMe) {
Subject subject = SecurityUtils.getSubject();
if (!subject.isAuthenticated()) {
try {
UserToken token = new UserToken(username, password, rememberMe, LoginType.PASSWORD);
subject.login(token);
return "登錄成功";
} catch (AuthenticationException e) {
e.printStackTrace();
return "登錄失敗";
}
} else {
return "已經(jīng)登錄成功";
}
}
/**
* 登錄接口 使用 code 替換 password,并去掉加解密
*
* @param username
* @param code
*/
@GetMapping("/doLoginByCode")
public String doLoginByCode(String username, String code, boolean rememberMe) {
Subject subject = SecurityUtils.getSubject();
if (!subject.isAuthenticated()) {
try {
UserToken token = new UserToken(username, code, rememberMe, LoginType.PHONE_CODE);
subject.login(token);
return "登錄成功";
} catch (AuthenticationException e) {
e.printStackTrace();
return "登錄失敗";
}
} else {
return "已經(jīng)登錄成功";
}
}
測(cè)試
使用賬號(hào)密碼登錄,之后退出
使用賬號(hào)驗(yàn)證碼登錄,之后退出

有用的話關(guān)注我的微信公眾號(hào)吧!