1.先上工具类MD5Util 目前真正用到的是直接MD5加密的方法 未使用自定义加工密码加密
package cn.cjq.util;
import cn.cjq.entity.User;
import java.security.MessageDigest;
import java.util.Random;
public class MD5Util {
/**
* 加工密码,和登录一致。
* @param user
* @return
*/
public static User md5Pswd(User user){
//密码为 username + '#' + password,然后MD5
user.setPassword(md5Pswd(user.getUsername(),user.getPassword()));
return user;
}
/**
* 字符串返回值
* @param email
* @param pswd
* @return
*/
public static String md5Pswd(String email ,String pswd){
pswd = String.format("%s#%s", email,pswd);
pswd = getMD5(pswd);
return pswd;
}
/**
* MD5 加密
* @param str
* @return
* @throws Exception
*/
public static String getMD5(String str) {
MessageDigest messageDigest = null;
try {
messageDigest = MessageDigest.getInstance("MD5");
messageDigest.reset();
messageDigest.update(str.getBytes("UTF-8"));
} catch (Exception e) {
System.out.println("MD5转换异常!message:%s"+e.getMessage());
}
byte[] byteArray = messageDigest.digest();
StringBuffer md5StrBuff = new StringBuffer();
for (int i = 0; i < byteArray.length; i++) {
if (Integer.toHexString(0xFF & byteArray[i]).length() == 1)
md5StrBuff.append("0").append(Integer.toHexString(0xFF & byteArray[i]));
else
md5StrBuff.append(Integer.toHexString(0xFF & byteArray[i]));
}
return md5StrBuff.toString();
}
/**
* 获取随机的数值。
* @param length 长度
* @return
*/
public static String getRandom620(Integer length){
String result = "";
Random rand = new Random();
int n = 20;
if(null != length && length > 0){
n = length;
}
boolean[] bool = new boolean[n];
int randInt = 0;
for(int i = 0; i < length ; i++) {
do {
randInt = rand.nextInt(n);
}while(bool[randInt]);
bool[randInt] = true;
result += randInt;
}
return result;
}
/**
* 加密解密算法 执行一次加密,两次解密
*/
public static String convertMD5(String inStr){
char[] a = inStr.toCharArray();
for (int i = 0; i < a.length; i++){
a[i] = (char) (a[i] ^ 't');
}
String s = new String(a);
return s;
}
}
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2.密码加密实例 注册时会使用(只需要将加密的密码存入数据库),传给UsernamePasswordToken的密码参数是未加密的密码,由密码匹配器去自动匹配
/**
* 注册 && 登录
* @param vcode 验证码
* @return
*/
@RequestMapping(value="subRegister")
@ResponseBody
@SystemControllerLog(description = "用户注册登陆")
public Response subRegister(@RequestParam(value="userName",required=false,defaultValue="") String userName,
@RequestParam(value="passWord",required=false,defaultValue="") String passWord,
@RequestParam(value="realname",required=false,defaultValue="") String realname,
@RequestParam(value="userType",required=false,defaultValue="") String userType,
@RequestParam(value="vcode",required=false,defaultValue="") String vcode)throws Exception{
//手机号不合法
if(!isMobileNO(userName)){
return new Response(ExceptionMsg.UserNameError);
}
//用户名已存在
User user = userServiceImpl.findByUserName(userName);
if(null != user){
return new Response(ExceptionMsg.UserNameUsed);
}
//验证码错误
KeyValue data=(KeyValue)stringRedisServiceImpl.get(userName);
if(data==null){
return new Response(ExceptionMsg.VcodeNotExists);
}else if(!data.getValue().equals(vcode)){
return new Response(ExceptionMsg.VcodeError);
}
//把密码md5
String password = MD5Util.getMD5(passWord);
//注册
User newuser =new User();
String uuid = UUID.randomUUID().toString().replaceAll("-", "");
newuser.setUerid(uuid);
newuser.setAddtime(new Date());
newuser.setCreateperson(uuid);
newuser.setUsername(userName);
newuser.setEnditime(new Date());
newuser.setIsdelete(0);
newuser.setStatus("1");
newuser.setPassword(password);
newuser.setRealname(realname);
newuser.setPhone(userName);
newuser.setDifference(1);
userServiceImpl.insertuser(newuser);
//登陆验证
UsernamePasswordToken token = new UsernamePasswordToken(userName,passWord,false);
SecurityUtils.getSubject().login(token);
Session sessions = SecurityUtils.getSubject().getSession();
sessions.setAttribute("user",newuser );
//清理内存
stringRedisServiceImpl.remove(userName);
return new Response(ExceptionMsg.SUCCESS);
}
--------------------------------------------------------------------------------------------------------------------------
3.shiro.xml配置密码匹配器 两个bean 配置在自定义的realm里
属性hashIterations的值目前为1,代表加密一次,其他值未试过,不明觉厉,待续
<!-- 凭证匹配器-->
<bean id="credentialsMatcher" class="cn.cjq.util.shiro.RetryLimitHashedCredentialsMatcher">
<constructor-arg ref="cacheManager"/>
<property name="hashAlgorithmName" value="md5"/>
<property name="hashIterations" value="1"/>
<property name="storedCredentialsHexEncoded" value="true"/>
</bean>
<bean id="myShiroRealm" class="cn.cjq.util.shiro.UserRealm">
<!-- 配置密码匹配器 -->
<property name="credentialsMatcher" ref="credentialsMatcher"/>
<property name="cachingEnabled" value="true"/>
<property name="authenticationCachingEnabled" value="true"/>
<property name="authenticationCacheName" value="authenticationCache"/>
<property name="authorizationCachingEnabled" value="true"/>
<property name="authorizationCacheName" value="authorizationCache"/>
</bean>
--------------------- ----------------------------------------------------------------------------------------------------------------
4.自定义的密码匹配器 目前使用的md5
package cn.cjq.util.shiro;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import java.util.concurrent.atomic.AtomicInteger;
public class RetryLimitHashedCredentialsMatcher extends
HashedCredentialsMatcher {
private Cache<String, AtomicInteger> passwordRetryCache;
public RetryLimitHashedCredentialsMatcher(CacheManager cacheManager) {
passwordRetryCache = cacheManager.getCache("passwordRetryCache");
}
@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
String username = (String)token.getPrincipal();
//retry count + 1
AtomicInteger retryCount = passwordRetryCache.get(username);
if(retryCount == null) {
retryCount = new AtomicInteger(0);
passwordRetryCache.put(username, retryCount);
}
if(retryCount.incrementAndGet() > 5) {
//if retry count > 5 throw
throw new ExcessiveAttemptsException();
}
boolean matches = super.doCredentialsMatch(token, info);
if(matches) {
//clear retry count
passwordRetryCache.remove(username);
}
return matches;
}
}