历史文章
(一)Spring Boot 集成 Shiro 权限管理与密码加盐
(三)Spring Boot 集成 Shiro 权限缓存功能
(四)Spring Boot 集成 Shiro 用户管理与登录保护
主要完成功能
用户可以使用账号密码登录或者手机验证码登录
shiro 有三种认证策略 AuthenticationStrategy
-
AtLeastOneSuccessfulStrategy
只要有一个成功即成功认证 -
FirstSuccessfulStrategy
第一个认证成功即成功认证 -
AllSuccessfulStrategy
全部认证成功即成功认证否则失败
默认是 AtLeastOneSuccessfulStrategy
修改 MyRealm 为 BaseRealm 并将 doGetAuthenticationInfo 交由子类实现
实现两个 Realm
- PasswordRealm
- PhoneCodeRealm
public class PasswordRealm extends BaseRealm {
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//获取用户的输入的账号
UserToken userToken = (UserToken) authenticationToken;
if (userToken.getLoginType() == LoginType.PASSWORD) {
//通过username从数据库中查找 User对象
User user = userMapper.selectByName(userToken.getUsername());
if (user == null) {
//没有返回登录用户名对应的SimpleAuthenticationInfo对象时,就会在LoginController中抛出UnknownAccountException异常
throw new UnknownAccountException("用户不存在");
}
//使用密码加盐的方式验证密码的安全,盐为用户注册时设置的用户名
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", //验证码,该验证码应该从数据库中查找
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 配置类
由之前的配置的单个 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()); //设置解密规则
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 "已经登录成功";
}
}
/**
* 登录接口 使用 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 "已经登录成功";
}
}
测试
使用账号密码登录,之后退出
使用账号验证码登录,之后退出