package com.util;
import com.domain.User;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.sun.crypto.provider.AESKeyGenerator;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.codec.CodecSupport;
import org.apache.shiro.codec.H64;
import org.apache.shiro.codec.Hex;
import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
import org.apache.shiro.crypto.hash.Md5Hash;
import java.security.Key;
/**
* User: cutter.li
* Date: 2014/6/27 0027
* Time: 16:49
* 備注: shiro進行加密解密的工具類封裝
*/
public final class EndecryptUtils {
/**
* base64進制加密
*
* @param password
* @return
*/
public static String encrytBase64(String password) {
Preconditions.checkArgument(!Strings.isNullOrEmpty(password), "不能為空");
byte[] bytes = password.getBytes();
return Base64.encodeToString(bytes);
}
* base64進制解密
* @param cipherText
public static String decryptBase64(String cipherText) {
Preconditions.checkArgument(!Strings.isNullOrEmpty(cipherText), "消息摘要不能為空");
return Base64.decodeToString(cipherText);
* 16進制加密
public static String encrytHex(String password) {
return Hex.encodeToString(bytes);
* 16進制解密
public static String decryptHex(String cipherText) {
return new String(Hex.decode(cipherText));
public static String generateKey()
{
AesCipherService aesCipherService=new AesCipherService();
Key key=aesCipherService.generateNewKey();
return Base64.encodeToString(key.getEncoded());
* 對密碼進行md5加密,并傳回密文和salt,包含在User對象中
* @param username 使用者名
* @param password 密碼
* @return 密文和salt
public static User md5Password(String username,String password){
Preconditions.checkArgument(!Strings.isNullOrEmpty(username),"username不能為空");
Preconditions.checkArgument(!Strings.isNullOrEmpty(password),"password不能為空");
SecureRandomNumberGenerator secureRandomNumberGenerator=new SecureRandomNumberGenerator();
String salt= secureRandomNumberGenerator.nextBytes().toHex();
//組合username,兩次疊代,對密碼進行加密
String password_cipherText= new Md5Hash(password,username+salt,2).toBase64();
User user=new User();
user.setPassword(password_cipherText);
user.setSalt(salt);
user.setUsername(username);
return user;
public static void main(String[] args) {
String password = "admin";
String cipherText = encrytHex(password);
System.out.println(password + "hex加密之後的密文是:" + cipherText);
String decrptPassword=decryptHex(cipherText);
System.out.println(cipherText + "hex解密之後的密碼是:" + decrptPassword);
String cipherText_base64 = encrytBase64(password);
System.out.println(password + "base64加密之後的密文是:" + cipherText_base64);
String decrptPassword_base64=decryptBase64(cipherText_base64);
System.out.println(cipherText_base64 + "base64解密之後的密碼是:" + decrptPassword_base64);
String h64= H64.encodeToString(password.getBytes());
System.out.println(h64);
String salt="7road";
String cipherText_md5= new Md5Hash(password,salt,4).toHex();
System.out.println(password+"通過md5加密之後的密文是:"+cipherText_md5);
System.out.println(generateKey());
System.out.println("==========================================================");
aesCipherService.setKeySize(128);
String aes_cipherText= aesCipherService.encrypt(password.getBytes(),key.getEncoded()).toHex();
System.out.println(password+" aes加密的密文是:"+aes_cipherText);
String aes_mingwen=new String(aesCipherService.decrypt(Hex.decode(aes_cipherText),key.getEncoded()).getBytes());
System.out.println(aes_cipherText+" aes解密的明文是:"+aes_mingwen);
}
package com.util.cache;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
import net.sf.ehcache.config.CacheConfiguration;
import net.sf.ehcache.store.MemoryStoreEvictionPolicy;
* Date: 2014/6/30 0030
* Time: 15:32
* 備注: ehcache的緩存工具類
public final class EhcacheUtil {
private static final CacheManager cacheManager = CacheManager.getInstance();
* 建立ehcache緩存,建立之後的有效期是1小時
private static Cache cache = new Cache(new CacheConfiguration("systemCache", 5000).memoryStoreEvictionPolicy(MemoryStoreEvictionPolicy.FIFO).timeoutMillis(300).timeToLiveSeconds( 60 * 60));
static {
cacheManager.addCache(cache);
public static void putItem(String key, Object item) {
if (cache.get(key) != null) {
cache.remove(key);
}
Element element = new Element(key, item);
cache.put(element);
public static void removeItem(String key) {
cache.remove(key);
public static void updateItem(String key, Object value) {
putItem(key, value);
public static Object getItem(String key) {
Element element= cache.get(key);
if(null!=element)
{
return element.getObjectValue();
return null;
String password_cipherText= new Md5Hash(password,username+salt,2).toHex();
* 通過username,password,salt,校驗密文是否比對 ,校驗規則其實在配置檔案中,這裡為了清晰,寫下來
* @param password 原密碼
* @param salt 鹽
* @param md5cipherText 密文
public static boolean checkMd5Password(String username,String password,String salt,String md5cipherText)
Preconditions.checkArgument(!Strings.isNullOrEmpty(md5cipherText),"md5cipherText不能為空");
String password_cipherText= new Md5Hash(password,username+salt,2).toHex();
return md5cipherText.equals(password_cipherText);
}
<bean id="myRealm" class="com.util.MysqlJdbcRealM">
<property name="credentialsMatcher" ref="passwordMatcher"></property>
</bean>
<bean id="passwordMatcher" class="com.util.LimitRetryHashedMatcher">
<property name="hashAlgorithmName" value="md5"></property>
<property name="hashIterations" value="2"></property>
<property name="storedCredentialsHexEncoded" value="true"></property>
</bean>
/**
* 使用者注冊
* @param entity
@Override
public ResponseEntity<Map> createSubmit(User entity) {
//加密使用者輸入的密碼,得到密碼的摘要和鹽,儲存到資料庫
User user = EndecryptUtils.md5Password(entity.getUsername(), entity.getPassword());
entity.setPassword(user.getPassword());
entity.setSalt(user.getSalt());
Map<String, Object> map = Maps.newHashMap();
try {
boolean createResult = service.modify(entity, OperationType.create);
map.put("success", createResult);
} catch (Exception e) {
e.printStackTrace();
return new ResponseEntity<Map>(map, HttpStatus.OK);
------------------------------------------------------------------華麗的分割線---------------------------------------------------------------------------------------------------
@RequestMapping(value = "login", method = RequestMethod.POST)
public ResponseEntity<Message> loginSubmit(String username, String password, String vcode, HttpServletRequest request) {
message.setSuccess();
validateLogin(message, username, password, vcode);
// String code = request.getSession().getAttribute(AppConstant.KAPTCHA_SESSION_KEY).toString();
// if (!vcode.equalsIgnoreCase(code)) {
// message.setCode(AppConstant.VALIDCODE_ERROR);
// message.setMsg("驗證碼錯誤");
// }
if (message.isSuccess()) {
Subject subject = SecurityUtils.getSubject();
subject.login(new UsernamePasswordToken(username, password,false));
if (subject.isAuthenticated()) {
message.setMsg("登入成功");
} else {
message.setCode(AppConstant.USERNAME_NOTEXIST);
message.setMsg("使用者名/密碼錯誤");
}
}
}catch (ExcessiveAttemptsException ex)
message.setCode(AppConstant.USERNAME_NOTEXIST);
message.setMsg("帳号被鎖定1小時");
ex.printStackTrace();
catch (AuthenticationException ex){
message.setMsg("使用者名/密碼錯誤");
finally {
return new ResponseEntity<Message>(message, HttpStatus.OK);
---------------------------------------------------------------認證的修改-------------------------------------------------------------------------------------
//登入認證
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
String username = String.valueOf(usernamePasswordToken.getUsername());
User user = userService.findByUserName(username);
SimpleAuthenticationInfo authenticationInfo = null;
if (null != user) {
String password = new String(usernamePasswordToken.getPassword());
//密碼校驗移交給了shiro的提供的一個接口實作類,是以這裡注釋掉
// if (EndecryptUtils.checkMd5Password(username,password,user.getSalt(),user.getPassword())) {
authenticationInfo = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName());
authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes(username+user.getSalt()));
return authenticationInfo;
5,重寫密碼校驗的方法
import com.util.cache.EhcacheUtil;
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 java.util.concurrent.atomic.AtomicInteger;
* Time: 15:22
* 備注: 限制登入次數,如果5次出錯,鎖定1個小時
public class LimitRetryHashedMatcher extends HashedCredentialsMatcher {
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
String username = (String) token.getPrincipal();
//retrycount + 1
Object element = EhcacheUtil.getItem(username);
if (element == null) {
EhcacheUtil.putItem(username, 1);
element=0;
}else{
int count=Integer.parseInt(element.toString())+1;
element=count;
EhcacheUtil.putItem(username,element);
AtomicInteger retryCount = new AtomicInteger(Integer.parseInt(element.toString()));
if (retryCount.incrementAndGet() > 5) {
//if retrycount >5 throw
throw new ExcessiveAttemptsException();
boolean matches = super.doCredentialsMatch(token, info);
if (matches) {
//clear retrycount
EhcacheUtil.removeItem(username);
return matches;
連續輸錯5次密碼之後,出現如下提示;
通過封裝常用的加密解密工具類,降低了對jdk自帶密碼工具類的學習成本;
可以靈活定義密碼的生成和判斷方式,并改變密碼判斷過程的邏輯;
no pays,no gains!
來源:http://www.cnblogs.com/snidget/p/3817763.html