package com.util; |
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; /** * User: cutter.li * 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; } } |
/** * 對密碼進行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).toHex(); User user=new User(); user.setPassword(password_cipherText); user.setSalt(salt); user.setUsername(username); return user; } /** * 經過username,password,salt,校驗密文是否匹配 ,校驗規則其實在配置文件中,這裏爲了清晰,寫下來 * @param username 用戶名 * @param password 原密碼 * @param salt 鹽 * @param md5cipherText 密文 * @return */ public static boolean checkMd5Password(String username,String password,String salt,String md5cipherText) { Preconditions.checkArgument(!Strings.isNullOrEmpty(username),"username不能爲空"); Preconditions.checkArgument(!Strings.isNullOrEmpty(password),"password不能爲空"); Preconditions.checkArgument(!Strings.isNullOrEmpty(md5cipherText),"md5cipherText不能爲空"); //組合username,兩次迭代,對密碼進行加密 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 * @return */ @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); try { // 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.setCode(AppConstant.USERNAME_NOTEXIST); message.setMsg("用戶名/密碼錯誤"); ex.printStackTrace(); } finally { return new ResponseEntity<Message>(message, HttpStatus.OK); } } ---------------------------------------------------------------認證的修改------------------------------------------------------------------------------------- //登陸認證 @Override 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,重寫密碼校驗的方法 sql
package com.util; 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; /** * User: cutter.li * Date: 2014/6/30 0030 * Time: 15:22 * 備註: 限制登陸次數,若是5次出錯,鎖定1個小時 */ public class LimitRetryHashedMatcher extends HashedCredentialsMatcher { @Override 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自帶密碼工具類的學習成本;apache
能夠靈活定義密碼的生成和判斷方式,並改變密碼判斷過程的邏輯;緩存