更改shiro安全管理配置
src/main/resources/spring-context-shiro.xml 其他非关键代码省略
...
<!-- 定义Shiro安全管理配置 -->
<bean id="systemAuthorizingRealm" class="com.manage.security.SystemAuthorizingRealm"/>
...
SystemAuthorizingRealm 代码
关键点1:
RetryLimitSimpleCredentialsMatcher 需要继承 SimpleCredentialsMatcher,不能继承HashedCredentialsMatcher (本人没有探究原因)
关键点2
SimpleAuthenticationInfo 的参数根据需要调整
PasswordHelper/validatePassword/2
/**
* 验证密码
*
* @param plainPassword 明文密码
* @param password 密文密码
* @return 验证成功返回true
*/
public static boolean validatePassword(String plainPassword, String password) {
String plain = EncodeUtils.unescapeHtml(plainPassword);
if (password.substring(0, DigestUtils.PBKDF2_SHA256.length()).equals(DigestUtils.PBKDF2_SHA256)) {
return DigestUtils.pbkdf2_sha256_verify(plain, password);
}
byte[] salt = getSalt(password, HASH_ALGORITHM);
byte[] hashPassword = null;
if (HASH_ALGORITHM.equals(DigestUtils.MD5)) {
hashPassword = DigestUtils.md5(plain.getBytes(), salt, HASH_INTERATIONS);
} else if (HASH_ALGORITHM.equals(DigestUtils.SHA1)) {
hashPassword = DigestUtils.sha1(plain.getBytes(), salt, HASH_INTERATIONS);
}
String tmpPwd = EncodeUtils.encodeHex(hashPassword);
if (salt != null) {
tmpPwd = EncodeUtils.encodeHex(salt) + tmpPwd;
}
return password.equals(tmpPwd);
}
SystemAuthorizingRealm 和 RetryLimitSimpleCredentialsMatcher 类
package com.manage.security;
import com.common.dict.ExpireTime;
import com.common.exception.BussinessException;
import com.common.security.shiro.UsernamePasswordToken;
import com.common.util.EncodeUtils;
import com.common.util.PasswordHelper;
import com.common.util.RSAEncrypt;
import com.common.util.StringUtils;
import com.core.core.constant.ConfigConstant;
import com.core.core.constant.Constant;
import com.core.core.constant.ResourceConstant;
import com.core.core.util.CacheUtils;
import com.core.core.util.ConfigUtils;
import com.core.core.util.ResourceUtils;
import com.core.core.util.ValidateUtils;
import com.core.sys.domain.Operator;
import com.core.sys.service.OperatorService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import javax.annotation.PostConstruct;
import java.util.concurrent.atomic.AtomicInteger;
import com.rd.ifaes.common.util.code.Converter;
//@Service
public class SystemAuthorizingRealm extends AuthorizingRealm {
private static final Logger LOGGER = LoggerFactory.getLogger(SystemAuthorizingRealm.class);
/**
* 登录失败缓存key前缀
*/
private static final String LOGIN_FAIL = "loginFail:";
@Autowired
private OperatorService operatorService;
/**
* 认证回调函数, 登录时调用
*/
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
LOGGER.debug("login submit username: {}", token.getUsername());
// 验证码校验
String vCode = StringUtils.isNull(token.getCaptcha());
if (vCode.isEmpty()) {
throw new BussinessException(ResourceUtils.get(ResourceConstant.VALID_CODE_NOTEMPTY),
BussinessException.TYPE_JSON);
}
if (!ValidateUtils.checkValidCode(vCode)) {
throw new BussinessException(ResourceUtils.get(ResourceConstant.VALID_CODE_ERROR),
BussinessException.TYPE_JSON);
}
//账户校验
Operator user = operatorService.getByLoginName(token.getUsername());
if (user == null || (user.getDeleteFlag() != null && Operator.ROLE_EQ.equals(user.getDeleteFlag()))) {
throw new UnknownAccountException();//没找到帐号
}
int formEncryptEnable = ConfigUtils.getInt(ConfigConstant.USER_LOGIN_FORM_ENCRYPT_ENABLE);
//对页面加密密码进行解密
if(formEncryptEnable == Constant.INT_ONE && token.getEncrypt() == Constant.INT_ONE){
String privateKey = ConfigUtils.getValue("rsa_private_key_pkcs8");
try {
byte[] pwd = RSAEncrypt.decrypt(
RSAEncrypt.loadPrivateKeyByStr(privateKey),
new BASE64Decoder().decodeBuffer(String.valueOf(token.getPassword()))
);
token.setPassword(Converter.bytesToChars(pwd));
} catch (Exception e) {
e.printStackTrace();
}
}
// 密码校验
String cacheKey = LOGIN_FAIL + token.getUsername();
AtomicInteger loginFailNum = CacheUtils.get(cacheKey, AtomicInteger.class);
if (loginFailNum == null) {
loginFailNum = new AtomicInteger(0);
}
if (loginFailNum.incrementAndGet() >= 5) {
throw new BussinessException(ResourceUtils.get(ResourceConstant.OPERATOR_IS_LOCKED),
BussinessException.TYPE_JSON);
}
CacheUtils.set(cacheKey, loginFailNum, ExpireTime.ONE_DAY);
return new SimpleAuthenticationInfo(
user, user.getPwd(),
ByteSource.Util.bytes("".getBytes()), getName());
}
/**
* 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用
*/
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection authcToken) {
Operator user = (Operator) authcToken.getPrimaryPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.setRoles(operatorService.findRoles(user.getUuid()));
info.setStringPermissions(operatorService.findPermissions(user.getUuid()));
return info;
}
@Override
public String getName() {
return "systemAuthorizingRealm";
}
@Override
public void clearCachedAuthorizationInfo(PrincipalCollection principals) {
super.clearCachedAuthorizationInfo(principals);
}
@Override
public void clearCachedAuthenticationInfo(PrincipalCollection principals) {
super.clearCachedAuthenticationInfo(principals);
}
@Override
public void clearCache(PrincipalCollection principals) {
super.clearCache(principals);
}
public void clearAllCachedAuthorizationInfo() {
getAuthorizationCache().clear();
}
public void clearAllCachedAuthenticationInfo() {
getAuthenticationCache().clear();
}
public void clearAllCache() {
clearAllCachedAuthenticationInfo();
clearAllCachedAuthorizationInfo();
}
/**
* 设定密码校验的Hash算法与迭代次数
*/
@PostConstruct
public void initCredentialsMatcher() {
setCredentialsMatcher(new RetryLimitSimpleCredentialsMatcher());
}
class RetryLimitSimpleCredentialsMatcher extends SimpleCredentialsMatcher {
@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
UsernamePasswordToken uptoken = (UsernamePasswordToken) token;
String userName = uptoken.getUsername();
String plainPassword = String.valueOf(uptoken.getPassword());
String password = String.valueOf(getCredentials(info));
boolean matches = PasswordHelper.validatePassword(plainPassword, password);
if (matches) {
//clear retry count
CacheUtils.del(LOGIN_FAIL + userName);
}
return matches;
}
}
}