一個登錄的具體流程:
1、前端發送公鑰的請求。
2、后台生產公鑰私鑰對,將公鑰返回前端,私鑰保存到session中。
3、前端拿到公鑰后,對用戶輸入的密碼進行md5加密,然后在對(md5加密后的密碼+密碼)進行rsa加密,發起登錄請求,將用戶名和加密后的密碼傳入后台進行校驗。
4、后台接受到加密后的密碼首先利用私鑰對密碼進行解密
5、對密碼進行完整的校驗即采用前32位的MD5碼與原始密碼(32位之后的值即為密碼)進行MD5加密后進行比較。
6、驗證通過后采用shiro的subject.login()將登陸驗證的工作轉交給shiro進行。
7、通過判斷shiro返回的異常信息來獲取校驗未成功的原因。
8、驗證通過檢查此用戶在其他地方時候登陸。方法為自定義一個session的緩存將登錄過的session與用戶名存入map中,從緩存中獲取此用戶名對應的緩存判斷sessionid是否一致,若不一致說明此次是重復登錄,將之前的session通過shiroSessionManager.getSessionDAO().delete(session)進行剔除。
9、返回登錄結果到前端。
具體代碼片段如下:
登錄校驗:
1 public Result checkLogin(String username, String encryptPassword, RSAPrivateKey privateKey){ 2 3 String decryptPassword = ""; 4 if (privateKey != null && encryptPassword != null) { 5 //解密后的密碼。由mds(pass) + pass 組成。 6 decryptPassword = RSAUtils.decrypt(privateKey, encryptPassword); 7 } 8 if(decryptPassword.length() < 32){ 9 return Result.error("用戶名或密碼驗證失敗。"); 10 } 11 //完整性校驗,防止篡改 12 if (!isComplate(decryptPassword)) { 13 return Result.error("密碼被篡改,驗證失敗。"); 14 } 15 decryptPassword = decryptPassword.substring(32); 16 String password = new Sha256Hash(decryptPassword).toHex(); 17 18 //密碼完整性校驗通過,交給shiro進行驗證 19 //進行驗證,這里可以捕獲異常,然后返回對應信息 20 try{ 21 Subject subject = SecurityUtils.getSubject(); 22 UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password); 23 subject.login(usernamePasswordToken); 24 //剔除重復登錄的用戶 25 SessionsSecurityManager securityManager = (SessionsSecurityManager) SecurityUtils.getSecurityManager(); 26 DefaultSessionManager shiroSessionManager = (DefaultSessionManager) securityManager.getSessionManager(); 27 Session session = sessionManager.getSession(username); 28 if(session != null){ 29 //不是同一個會話,進行剔除 30 if(!subject.getSession().getId().equals(session.getId())){ 31 shiroSessionManager.getSessionDAO().delete(session); 32 sessionManager.removeSession(username);//緩存中移除 33 } 34 } 35 //將新的用戶和session加入sessionManager中 36 sessionManager.addSession(username, subject.getSession()); 37 38 }catch(IncorrectCredentialsException ice){ 39 return Result.error("用戶名或密碼驗證失敗"); 40 }catch(UnknownAccountException uae){ 41 return Result.error("賬號不存在"); 42 }catch(LockedAccountException uae){ 43 return Result.error("賬號被鎖定"); 44 }catch(ExcessiveAttemptsException uae){ 45 return Result.error("操作頻繁,請稍后再試"); 46 }catch(ExpiredCredentialsException ece){ 47 return Result.error("賬號已過期,請重新登錄"); 48 } 49 return Result.ok(); 50 }
UserManager
@Component public class UserManager { @Autowired IFetchUser query; private ConcurrentMap<String, User> map = new ConcurrentHashMap<String, User>(); public User getUser(String name){ User user = map.get(name); if(user == null){ //去庫中查詢 user = query.queryFromDb(name); if(user != null){ map.put(name, user); } } return user; } public void removeUser(String name){ map.remove(name); } }
Shiro的配置
@Configuration public class SysShiroConfiguration { @Bean public MemorySessionDAO memorySessionDAO(){ MemorySessionDAO memorySessionDAO = new MemorySessionDAO(); return memorySessionDAO; } @Bean public WebSessionManager webSessionManager(){ DefaultWebSessionManager webSessionManager = new DefaultWebSessionManager(); webSessionManager.setSessionDAO(memorySessionDAO()); return webSessionManager; } @Bean public MemoryConstrainedCacheManager memoryConstrainedCacheManager(){ MemoryConstrainedCacheManager cacheManager = new MemoryConstrainedCacheManager(); return cacheManager; } //將自己的驗證方式加入容器 @Bean public CustomShiroRealm customShiroRealm() { CustomShiroRealm customShiroRealm = new CustomShiroRealm(); return customShiroRealm; } //權限管理,配置主要是Realm的管理認證 @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(customShiroRealm()); securityManager.setCacheManager(memoryConstrainedCacheManager()); securityManager.setSessionManager(webSessionManager()); return securityManager; } //Filter工廠,設置對應的過濾條件和跳轉條件 @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); Map<String,String> map = new HashMap<String, String>(); //登出 // map.put("/logout","logout"); // map.put("/test/**","anon"); // map.put("/pages/index.jsp","anon"); // map.put("/index.jsp","anon"); // map.put("/statics/**","anon"); //對所有用戶認證 map.put("/**","anon"); //登錄 shiroFilterFactoryBean.setLoginUrl("/login"); //首頁 shiroFilterFactoryBean.setSuccessUrl("/home"); //錯誤頁面,認證不通過跳轉 shiroFilterFactoryBean.setUnauthorizedUrl("/error"); shiroFilterFactoryBean.setFilterChainDefinitionMap(map); return shiroFilterFactoryBean; } @Bean public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){ return new LifecycleBeanPostProcessor(); } @Bean @DependsOn({"lifecycleBeanPostProcessor"}) public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){ DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); advisorAutoProxyCreator.setProxyTargetClass(true); return advisorAutoProxyCreator; } //加入注解的使用,不加入這個注解不生效 @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } }