菜鳥第一次寫博客。不足之處多諒解
shiro需要的jar
<!--導入shiro-web的依賴 --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>1.6.0</version> </dependency> <!-- 導入shiro-core的依賴 --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.6.0</version> </dependency> <!-- 導入shiro-spring 的依賴--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.6.0</version> </dependency>
1,添加shiro配置文件:applicationContext-shiro.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/p http://www.springframework.org/schema/p/spring-p.xsd "> <context:component-scan base-package="cn.ljs.realm"/> <!-- 1. 配置securityManager,也就是shiro的核心。 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="myRealm" /> <!-- 緩存管理器 --> <property name="cacheManager" ref="cacheManager" /> <property name="sessionManager" ref="sessionManager"></property> </bean> <!-- 2. 配置cacheManager(緩存管理) --> <bean id="cacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager"> </bean> <!-- 3. 配置Realm,自己定義的shiroRealm,必須實現org.apache.shiro.realm.Realm這個接口 --> <bean id="myRealm" class="cn.ljs.realm.MyRealm"></bean> <!-- 4.配置lifecycleBeanPostProcessor, 可以自動的來調用配置在spring IOC 容器中shiro bean的生命周期方法 --> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" /> <!-- 5.啟用IOC容器中使用shiro的注解,但必須在配置 lifecycleBeanPostProcessor才可以使用--> <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor" /> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager"/> </bean> <!-- 存儲登錄用戶的信息 --> <bean id="sessionDao" class="org.apache.shiro.session.mgt.eis.MemorySessionDAO"/> <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager"> <!-- 設置超時時間 --> <property name="globalSessionTimeout" value="14400000"/> <property name="sessionDAO" ref="sessionDao"/> <!-- 檢測掃描信息時間間隔,單位為毫秒--> <property name="sessionValidationInterval" value="60000"/> <!-- 是否開啟掃描 --> <property name="sessionValidationSchedulerEnabled" value="false"/> <!-- 去除服務首次啟動后訪問首頁地址欄帶JSESSIONID --> <property name="sessionIdUrlRewritingEnabled" value="false"></property> <property name="sessionIdCookie" ref="simpleCookie"/> </bean>
<!--指定本系統SESSIONID, 默認為: JSESSIONID 問題: 與SERVLET容器名沖突, 如JETTY, TOMCAT 等默認JSESSIONID,
當跳出SHIRO SERVLET時如ERROR-PAGE容器會為JSESSIONID重新分配值導致登錄會話丟失! -->
<!-- sessionIdCookie的實現,用於重寫覆蓋容器默認的JSESSIONID --> <bean id="simpleCookie" class="org.apache.shiro.web.servlet.SimpleCookie"> <!-- 設置Cookie名字, 默認為: JSESSIONID 問題: 與SERVLET容器名沖突, 如JETTY, TOMCAT 等默認JSESSIONID, 當跳出SHIRO SERVLET時如ERROR-PAGE容器會為JSESSIONID重新分配值導致登錄會話丟失! --> <property name="name" value="SHIRO-COOKIE"/> <!-- JSESSIONID的path為/用於多個系統共享JSESSIONID --> <!-- <property name="path" value="/"/> --> <!-- 瀏覽器中通過document.cookie可以獲取cookie屬性,設置了HttpOnly=true,在腳本中就不能的到cookie,可以避免cookie被盜用 --> <property name="httpOnly" value="true"/> </bean> <!-- 6. 配置shiroFilter 6.1 id必須和web.xml 文件中配置的DelegatingFilterProxy的filter-name一致 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager" /> <property name="loginUrl" value="/index.jsp" /> <!-- <property name="successUrl" value="/user/list.do" />--> <!-- <property name="unauthorizedUrl" value="/index.jsp" /> --> <!-- shiro的過濾器 - 過濾規則 常用的過濾器: authc:必須認證才能通過 anon:游客可以通過 logout:注銷的請求 --> <property name="filterChainDefinitions"> <value> /loginController/login=anon<!-- 登錄接口放行 -->/login=anon /css/**=anon /image/**=anon /js/**=anon <!-- 除了以上放行,以下全部攔截,必須登錄認證 --> /**=authc </value> </property> </bean> <!-- <context:component-scan base-package="maven.spring.shiro"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" /> </context:component-scan> --> </beans>
2,spring-mvc添加shiro配置
<!--Shiro配置--> <aop:config proxy-target-class="true"></aop:config> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager" /> </bean>
3,web.xml添加shiro過濾器
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1" metadata-complete="true"> <!--Shiro過濾器--> <filter> <filter-name>shiroFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <param-name>transformWsdlLocations</param-name> <param-value>true</param-value> </init-param> <!-- <!–在from文件上傳springContext接收–> --> <init-param> <param-name>targetFilterLifecycle</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>shiroFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 加載spring容器配置 --> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <!-- 設置spring容器加載所有的配置文件路徑 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:spring*.xml
<!-- 配置文件名字統一就不用下面這個,這個是shiro的配置文件 --> classpath:applicationContext*.xml </param-value> </context-param> <context-param> <param-name/> <param-value/> </context-param> <!-- 配置springMVC核心控制器 --> <servlet> <servlet-name>springMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 配置初始配置化文件,前面contextConfigLocation看情況二選一 --> <init-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:spring-mvc.xml </param-value> </init-param> <!-- 啟動加載一次 --> <load-on-startup>1</load-on-startup> </servlet> <!--為DispatcherServlet建立映射 --> <servlet-mapping> <servlet-name>springMVC</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <!-- 解決工程編碼過濾器 --> <filter> <filter-name>characterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>characterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter> <filter-name>CORS</filter-name> <filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class> <init-param> <param-name>cors.allowGenericHttpRequests</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>cors.allowOrigin</param-name> <param-value>*</param-value> </init-param> <init-param> <param-name>cors.allowSubdomains</param-name> <param-value>false</param-value> </init-param> <init-param> <param-name>cors.supportedMethods</param-name> <param-value>GET, HEAD, POST, OPTIONS</param-value> </init-param> <init-param> <param-name>cors.supportedHeaders</param-name> <param-value>*</param-value> </init-param> <init-param> <param-name>cors.exposedHeaders</param-name> <param-value>X-Test-1, X-Test-2</param-value> </init-param> <init-param> <param-name>cors.supportsCredentials</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>cors.maxAge</param-name> <param-value>3600</param-value> </init-param> </filter> <filter-mapping> <filter-name>CORS</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>
4,自定義一個 myRealm,名字要跟配置一樣
里面核心代碼:用戶認證核心邏輯看需要什么,這里只寫大致邏輯
package cn.ljs.realm; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.SimpleAuthenticationInfo; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.session.Session; import org.apache.shiro.session.mgt.eis.SessionDAO; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.subject.Subject; import org.apache.shiro.subject.support.DefaultSubjectContext; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.stereotype.Service; import cn.ljs.entity.SysResc; import cn.ljs.entity.SysRole; import cn.ljs.entity.SysUser; import cn.ljs.service.SysUserLoginService; import java.net.SocketException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; /** * Shiro的數據源 */ @Component public class MyRealm extends AuthorizingRealm {/** * 權限控制 - 每次驗證權限時都會調用 * @param principals * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { SysUser user = (SysUser) principals.getPrimaryPrincipal(); //從用戶對象中直接獲得權限列表 //List<SysResc> rescs = user.getSysResc();
String string = user.getSysRole().getRescs();
SysResc sysResc = new SysResc();
sysResc.setRquanxian(string);
List<SysResc> rescs = new ArrayList<>();
rescs.add(sysResc);
if(rescs != null || role!=null){ SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); for (SysResc resc : rescs) { if(resc.getRquanxian() != null && !"".equals(resc.getRquanxian())) { simpleAuthorizationInfo.addStringPermission(resc.getRquanxian()); } }
//simpleAuthorizationInfo.addRole(rolename);//也可以根據需要存入角色名字,名字從user對象獲取 return simpleAuthorizationInfo; } return null; } /** * 身份認證 * @param token * @return * @throws AuthenticationException */
@Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { //獲得登錄時的用戶名 String username = (String) token.getPrincipal(); SysUser user=new SysUser();
//通過用戶名查詢用戶信息
List<Map<String, Object>> list = new ArrayList<Map<String,Object>>();
Map<String, Object> map=new HashMap<String, Object>();
list = userLogin.queryUser(username);
for(int i=0;i<list.size();i++) {//數據就用了三張表,用戶表,角色表,權限表,沒有用中間表,權限表是一個字典表,
map=list.get(i);
//查出來的賬號信息全部放入實體類就行,比如。
user.setName((String)map.get("name"));//名字
//角色信息
SysRole role=new SysRole();
//登錄時一並查出來的角色信息放進去,就列舉一個,需要什么放什么,下面的權限我這里是用數字代替,比如:1,2,3。最后有一個圖片,是我這里使用的權限字典表
role.setRescs((String) map2.get("rescs"));//權限,這里存入的信息在后面會用到。比如上面的權限控制:doGetAuthorizationInfo
user.setSysRole(role);
}
if(user != null){//將用戶認證信息存入shiro框架 SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user, user.getPassword(), this.getName()); return simpleAuthenticationInfo; }else { } return null; } }
5,登錄方法調用,以及保證線上同一個賬號只允許一個在線,其他的通通擠下線,
有兩個登錄方法:
1,第一個是做了登錄次數限制,密碼錯誤三次十分鍾之內不可以再次嘗試登錄,如果密碼輸入錯誤總次數大於十次,賬號會被鎖定。需要在用戶表增加三個字段,再根據項目做調整。
package cn.ljs.controller; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.IncorrectCredentialsException; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.mgt.DefaultSecurityManager; import org.apache.shiro.session.Session; import org.apache.shiro.session.mgt.DefaultSessionManager; import org.apache.shiro.subject.Subject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import cn.ljs.entity.ClientIP; import cn.ljs.entity.Logtable; import cn.ljs.entity.SysUser; import cn.ljs.service.LogtableService; import cn.ljs.service.SysUserLoginService; import cn.ljs.util.Loggings; import cn.ljs.util.MD5; import cn.ljs.util.TimeUtil; @Controller @RequestMapping("/loginController") public class LoginController { @Autowired private SysUserLoginService userLogin; //private UserManageService userservice; @Autowired private LogtableService logtableService; @RequestMapping("/login") @ResponseBody public List<String> login(String username, String password, Model model,HttpServletRequest request, HttpServletResponse response){ List<String> list = new ArrayList<>(); String ret=""; String ip = request.getHeader("x-forwarded-for"); if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_CLIENT_IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_X_FORWARDED_FOR"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } //交給shiro進行認證 Subject subject = SecurityUtils.getSubject(); //對密碼加密處理 password = MD5.makeMD5(password); //獲得系統時間,與賬號最后登錄時間對比,密碼錯誤超過3次禁用10分鍾,超過10次鎖定賬號 Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat( " yyyy-MM-dd HH:mm:ss " ); String nowTime = sdf.format(date); Date time = null; try { time = sdf.parse( nowTime ); } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } //獲取賬號狀態,判斷是否已被鎖定,statesum大於10則鎖定, SysUser users=new SysUser(); List<Map<String, Object>> userlist = new ArrayList<Map<String,Object>>(); Map<String, Object> map=new HashMap<String, Object>(); userlist = userLogin.queryUser(username); for(int i=0;i<userlist.size();i++) { map=userlist.get(i); users.setId((Integer) map.get("id")); users.setState((Integer) map.get("state")); users.setStatesum((Integer) map.get("statesum")); users.setLogintime((Date) map.get("logintime")); } Integer state2 = users.getState(); Integer statesum2 = users.getStatesum(); if(statesum2==null) { statesum2=0; } //state=3,logintime<10分鍾為臨時鎖定,十分鍾后才可以登錄 if(users.getLogintime()!=null && state2!=null) { //計算時間差(分鍾) long datePoor = TimeUtil.getDatePoor(users.getLogintime()); //時間超過十分鍾后更新當前賬號狀態 if (statesum2>9) { ret = "賬號已被鎖定,請聯系管理員!"; list.add(ret); return list; }else if(state2>2 && datePoor<10) { ret = "賬號已被鎖定十分鍾,請稍后重試!"; list.add(ret); return list; }else if(datePoor>10){ users.setState(null); //鎖定十分鍾后,清空state次數 userLogin.updateState(users); } } if(!subject.isAuthenticated()){ UsernamePasswordToken token = new UsernamePasswordToken(username, password); try { subject.login(token); // 剔除其他此賬號在其它地方登錄 List<Session> loginedList = getLoginedSession(subject); for (Session session : loginedList) { session.stop(); } } catch (UnknownAccountException ex1) { ex1.printStackTrace(); ret = "賬號或密碼錯誤,請重新輸入!"; list.add(ret); return list; } catch (IncorrectCredentialsException ex2) { ex2.printStackTrace(); //重新查詢賬號狀態 SysUser u = new SysUser(); List<Map<String, Object>> userlists = new ArrayList<Map<String,Object>>(); Map<String, Object> maps=new HashMap<String, Object>(); userlists = userLogin.queryUser(username); for(int i=0;i<userlists.size();i++) { maps=userlists.get(i); u.setId((Integer) maps.get("id")); u.setState((Integer) maps.get("state")); u.setStatesum((Integer) maps.get("statesum")); u.setLogintime((Date) maps.get("logintime")); u.setLasttime((Date) maps.get("lasttime")); } Integer state = u.getState();//錯誤次數 Integer statesum = u.getStatesum(); if(state==null && statesum==null) { state=1; u.setState(state); u.setStatesum(state); u.setLogintime(time);//登錄失敗,密碼錯誤,當前登錄時間 userLogin.updateState(u); ret = "賬號或密碼錯誤,您還有"+(3-state)+"次輸入機會!"; list.add(ret); return list; }else if(statesum>8) { statesum=statesum+1; state=3; u.setStatesum(statesum);// u.setState(state); u.setLogintime(time);//登錄失敗,密碼錯誤,當前登錄時間 userLogin.updateState(u); ret = "賬號已被鎖定,請聯系管理員!"; list.add(ret); return list; }else if (state == null) { state=1; statesum=statesum+1; u.setState(state); u.setStatesum(statesum); u.setLogintime(time);//登錄失敗,密碼錯誤,當前登錄時間 userLogin.updateState(u); ret = "賬號或密碼錯誤,您還有"+(3-state)+"次輸入機會!"; list.add(ret); return list; }else if (statesum == null) { statesum=1; u.setState(state); u.setStatesum(state); u.setLogintime(time);//登錄失敗,密碼錯誤,當前登錄時間 userLogin.updateState(u); ret = "賬號或密碼錯誤,您還有"+(3-state)+"次輸入機會!"; list.add(ret); return list; }else if(state<2) { state=state+1; statesum=statesum+1; u.setState(state);// u.setStatesum(statesum);//更新失敗總次數 u.setLogintime(time);//登錄失敗,密碼錯誤,當前登錄時間 userLogin.updateState(u); ret = "賬號或密碼錯誤,您還有"+(3-state)+"次輸入機會!"; list.add(ret); return list; }else{ state=state+1; statesum=statesum+1; u.setState(state); u.setStatesum(statesum);// u.setLogintime(time);//登錄失敗,密碼錯誤,當前登錄時間 userLogin.updateState(u); ret = "賬號已被鎖定十分鍾,請稍后重試!"; list.add(ret); return list; } }catch (AuthenticationException ex3) { ret = "賬號或密碼錯誤,請重新輸入!"; list.add(ret); return list; } } Integer state = users.getState(); Integer statesum = users.getStatesum(); if(state!=null) { if(state>2) { ret = "賬號已被鎖定十分鍾,請稍后重試!"; list.add(ret); return list; }else if ( statesum>9) { ret = "賬號已被鎖定,請聯系管理員!"; list.add(ret); return list; }else { ret="登錄成功"; list.add(ret); } }else { ret="登錄成功"; list.add(ret); } //獲得當前認證的對象 SysUser user = (SysUser) subject.getPrincipal(); user.setState(null); user.setStatesum(null); user.setLogintime(null); user.setLasttime(time); user.setLastip(ip); userLogin.updateState(user); //將權限存入session,頁面通過Ajax調用存入session的權限 HttpSession session = request.getSession(); String rolename = user.getSysRole().getRolename(); if(!session.equals("")||session!=null) { session.setAttribute("user", user.getSysResc()); session.setAttribute("id", user.getId()); session.setAttribute("role", user.getRoles()); session.setAttribute("account", user.getAccount()); session.setAttribute("password", user.getPassword()); session.setAttribute("name", user.getName()); session.setAttribute("rolename", rolename); } return list; } //遍歷同一個賬戶的session private List<Session> getLoginedSession(Subject currentUser) { Collection<Session> list = ((DefaultSessionManager) ((DefaultSecurityManager) SecurityUtils .getSecurityManager()).getSessionManager()).getSessionDAO() .getActiveSessions(); List<Session> loginedList = new ArrayList<Session>(); SysUser loginUser = (SysUser) currentUser.getPrincipal(); for (Session session : list) { Subject s = new Subject.Builder().session(session).buildSubject(); if (s.isAuthenticated()) { SysUser user = (SysUser) s.getPrincipal(); if (user.getId()==(loginUser.getId())) { if (!session.getId().equals(currentUser.getSession().getId())) { loginedList.add(session); } } } } return loginedList; } }
2,第二個沒有限制,只要認證成功就可以。
package cn.ljs.controller; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.IncorrectCredentialsException; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.mgt.DefaultSecurityManager; import org.apache.shiro.session.Session; import org.apache.shiro.session.mgt.DefaultSessionManager; import org.apache.shiro.subject.Subject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import cn.ljs.entity.ClientIP; import cn.ljs.entity.Logtable; import cn.ljs.entity.SysUser; import cn.ljs.service.LogtableService; import cn.ljs.service.SysUserLoginService; import cn.ljs.util.Loggings; import cn.ljs.util.MD5; import cn.ljs.util.TimeUtil; @Controller @RequestMapping("/loginController") public class LoginController { @Autowired private SysUserLoginService userLogin; //private UserManageService userservice; @Autowired private LogtableService logtableService; @RequestMapping("/login") @ResponseBody public List<String> login(String username, String password, Model model,HttpServletRequest request, HttpServletResponse response){ List<String> list = new ArrayList<>(); String ret="";//登錄返回值隨自己定義, //交給shiro進行認證 Subject subject = SecurityUtils.getSubject();if(!subject.isAuthenticated()){ UsernamePasswordToken token = new UsernamePasswordToken(username, password); try { subject.login(token); // 剔除其他此賬號在其它地方登錄 List<Session> loginedList = getLoginedSession(subject); for (Session session : loginedList) { session.stop(); } } catch (UnknownAccountException ex1) { ex1.printStackTrace();
ret = "賬號錯誤";
} catch (IncorrectCredentialsException ex2) { ex2.printStackTrace();
ret = "密碼錯誤";
}catch (AuthenticationException ex3) { //ex3.printStackTrace();
//ret = "該賬號不存在";
ret = "賬號或密碼錯誤,請重新輸入!"; list.add(ret); return list; } } return list; } //遍歷同一個賬戶的session private List<Session> getLoginedSession(Subject currentUser) { Collection<Session> list = ((DefaultSessionManager) ((DefaultSecurityManager) SecurityUtils .getSecurityManager()).getSessionManager()).getSessionDAO() .getActiveSessions(); List<Session> loginedList = new ArrayList<Session>(); SysUser loginUser = (SysUser) currentUser.getPrincipal(); for (Session session : list) { Subject s = new Subject.Builder().session(session).buildSubject(); if (s.isAuthenticated()) { SysUser user = (SysUser) s.getPrincipal(); if (user.getId()==(loginUser.getId())) { if (!session.getId().equals(currentUser.getSession().getId())) { loginedList.add(session); } } } } return loginedList; } }
6,計算時間工具類,獲取系統當前時間和賬號登錄失敗時間,大於等於10分鍾允許重新登錄,
package cn.ljs.util; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; public class TimeUtil { /* public static long getDatePoor(Date endDate, Date nowDate) { long nd = 1000 * 24 * 60 * 60; long nh = 1000 * 60 * 60; long nm = 1000 * 60; // long ns = 1000; // 獲得兩個時間的毫秒時間差異 long diff = endDate.getTime() - nowDate.getTime(); // 計算差多少天 //long day = diff / nd; // 計算差多少小時 //long hour = diff % nd / nh; // 計算差多少分鍾 long min = diff % nd % nh / nm; // 計算差多少秒//輸出結果 // long sec = diff % nd % nh % nm / ns; return min; }*/ public static long getDatePoor(Date endDate) { String data = endDate.toString(); long dateMethod = dateMethod(data); System.out.println("分鍾數:" + dateMethod); // currTime(); return dateMethod; } /** * 計算時間差(單位:分鍾) * @param lastReceiveTime * @return */ private static long dateMethod(String lastReceiveTime) { System.out.println("最后時間" + lastReceiveTime); DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); try { Date date1 = df.parse(currTime()); Date date2 = df.parse(lastReceiveTime); long diff = date1.getTime() - date2.getTime(); System.out.println("毫秒數:" + diff); //計算兩個時間之間差了多少分鍾 long minutes = diff / (1000 * 60); return minutes; } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return 0; } /** * 得到當前時間 yyyy-MM-dd HH:mm:ss格式 * @return 當前時間 */ private static String currTime() throws Exception{ DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date date = new Date(); String currTime = df.format(date); System.out.println("當前時間" + currTime); return currTime; } }
7,jsp使用shiro標簽,頁面需要引入shiro標簽庫,
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %> //測試是否成功,name是后台查詢出來存入shiro的user對象里的值,頁面需要什么,后台就存什么。其他標簽可自行百度 <shiro:user> <shiro:principal property="name"</shiro:principal> </shiro:user>
列舉如下幾個頁面常用標簽:更多標簽請百度。。。
<shiro:hasAnyRoles name="管理員,普通角色">
123
</shiro:hasAnyRoles>//如果擁有管理員或者普通角色的其中一個,123就會顯示。沒有就不顯示
<shiro:hasPermission name="1">//如果擁有權限1,456就會顯示,沒有就不顯示
456
</shiro:hasPermission>
<shiro:lacksPermission name="1">//除了有1權限的不會顯示789,其他都能顯示
789
</shiro:lacksPermission>
controller注解式:
@RequiresRoles("超級管理員")//擁有該角色可以訪問
更多注解式使用方法:https://blog.csdn.net/qi923701/article/details/75224554
貼上我的權限字典表,上面標簽里的name值對應下圖中的rquanxian值。正常的話是五張表,用戶表,角色表,權限表,用戶和角色中間表,角色和權限中間表,這里看項目需求,怎么簡單方便怎么來。