關於ssm整合shiro框架及shiro的session與spring的session沖突導致認證失效、用戶會話信息丟失、shiro會自動退出認證問題


菜鳥第一次寫博客。不足之處多諒解

 

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>
        <!-- &lt;!&ndash;在from文件上傳springContext接收&ndash;&gt; -->
        <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值。正常的話是五張表,用戶表,角色表,權限表,用戶和角色中間表,角色和權限中間表,這里看項目需求,怎么簡單方便怎么來。

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM