1. 程式人生 > >SpringBoot學習:整合shiro(身份認證和許可權認證),使用EhCache快取

SpringBoot學習:整合shiro(身份認證和許可權認證),使用EhCache快取

專案下載地址:http://download.csdn.NET/detail/aqsunkai/9805821

(一)在pom.xml中新增依賴:

<properties>
    <shiro.version>1.3.2</shiro.version>
</properties>
<!--shiro start-->
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-core</artifactId>
      <version>${shiro.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-web</artifactId>
      <version>${shiro.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-ehcache</artifactId>
      <version>${shiro.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-spring</artifactId>
      <version>${shiro.version}</version>
    </dependency>
<!--shiro end-->
下面是資料庫的表,這裡主要涉及到五張表:使用者表,角色表(使用者所擁有的角色),許可權表(角色所涉及到的許可權),使用者-角色表(使用者和角色是多對多的),角色-許可權表(角色和許可權是多對多的)。sql語句如下:
-- ----------------------------
-- Table structure for sys_permission
-- ----------------------------
DROP TABLE IF EXISTS `sys_permission`;
CREATE TABLE `sys_permission` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵ID',
  `url` varchar(256) DEFAULT NULL COMMENT 'url地址',
  `name` varchar(64) DEFAULT NULL COMMENT 'url描述/名稱',
   parent_id int(11) DEFAULT NULL COMMENT '父節點許可權ID',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of sys_permission
-- ----------------------------
INSERT INTO `sys_permission` VALUES ('10', '/member/changeSessionStatus.shtml', '使用者Session踢出',null);
INSERT INTO `sys_permission` VALUES ('11', '/member/forbidUserById.shtml', '使用者啟用&禁止',null);
INSERT INTO `sys_permission` VALUES ('12', '/member/deleteUserById.shtml', '使用者刪除',null);
INSERT INTO `sys_permission` VALUES ('13', '/permission/addPermission2Role.shtml', '許可權分配',null);
INSERT INTO `sys_permission` VALUES ('14', '/role/clearRoleByUserIds.shtml', '使用者角色分配清空',null);
INSERT INTO `sys_permission` VALUES ('15', '/role/addRole2User.shtml', '角色分配儲存',null);
INSERT INTO `sys_permission` VALUES ('16', '/role/deleteRoleById.shtml', '角色列表刪除',null);
INSERT INTO `sys_permission` VALUES ('17', '/role/addRole.shtml', '角色列表新增',null);
INSERT INTO `sys_permission` VALUES ('18', '/role/index.shtml', '角色列表',null);
INSERT INTO `sys_permission` VALUES ('19', '/permission/allocation.shtml', '許可權分配',null);
INSERT INTO `sys_permission` VALUES ('20', '/role/allocation.shtml', '角色分配',null);
INSERT INTO `sys_permission` VALUES ('4', '/permission/index.shtml', '許可權列表',null);
INSERT INTO `sys_permission` VALUES ('6', '/permission/addPermission.shtml', '許可權新增',null);
INSERT INTO `sys_permission` VALUES ('7', '/permission/deletePermissionById.shtml', '許可權刪除',null);
INSERT INTO `sys_permission` VALUES ('8', '/member/list.shtml', '使用者列表',null);
INSERT INTO `sys_permission` VALUES ('9', '/member/online.shtml', '線上使用者',null);

-- ----------------------------
-- Table structure for sys_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵ID',
  `name` varchar(32) DEFAULT NULL COMMENT '角色名稱',
  `type` varchar(10) DEFAULT NULL COMMENT '角色型別',
   PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of sys_role
-- ----------------------------
INSERT INTO `sys_role` VALUES ('1', '系統管理員', '100004');
INSERT INTO `sys_role` VALUES ('3', '許可權角色', '100001');
INSERT INTO `sys_role` VALUES ('4', '使用者中心', '100002');
INSERT INTO `sys_role` VALUES ('0', '角色管理', '100003');

-- ----------------------------
-- Table structure for sys_role_permission
-- ----------------------------
DROP TABLE IF EXISTS `sys_role_permission`;
CREATE TABLE `sys_role_permission` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵ID',
  `rid` varchar(64) DEFAULT NULL COMMENT '角色ID',
  `pid` varchar(64) DEFAULT NULL COMMENT '許可權ID',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of sys_role_permission
-- ----------------------------
INSERT INTO `sys_role_permission` VALUES ('1', '4', '8');
INSERT INTO `sys_role_permission` VALUES ('10', '3', '14');
INSERT INTO `sys_role_permission` VALUES ('11', '3', '15');
INSERT INTO `sys_role_permission` VALUES ('12', '3', '16');
INSERT INTO `sys_role_permission` VALUES ('13', '3', '17');
INSERT INTO `sys_role_permission` VALUES ('14', '3', '18');
INSERT INTO `sys_role_permission` VALUES ('15', '3', '19');
INSERT INTO `sys_role_permission` VALUES ('16', '3', '20');
INSERT INTO `sys_role_permission` VALUES ('17', '1', '4');
INSERT INTO `sys_role_permission` VALUES ('18', '1', '6');
INSERT INTO `sys_role_permission` VALUES ('19', '1', '7');
INSERT INTO `sys_role_permission` VALUES ('2', '4', '9');
INSERT INTO `sys_role_permission` VALUES ('20', '1', '8');
INSERT INTO `sys_role_permission` VALUES ('21', '1', '9');
INSERT INTO `sys_role_permission` VALUES ('22', '1', '10');
INSERT INTO `sys_role_permission` VALUES ('23', '1', '11');
INSERT INTO `sys_role_permission` VALUES ('24', '1', '12');
INSERT INTO `sys_role_permission` VALUES ('25', '1', '13');
INSERT INTO `sys_role_permission` VALUES ('26', '1', '14');
INSERT INTO `sys_role_permission` VALUES ('27', '1', '15');
INSERT INTO `sys_role_permission` VALUES ('28', '1', '16');
INSERT INTO `sys_role_permission` VALUES ('29', '1', '17');
INSERT INTO `sys_role_permission` VALUES ('3', '4', '10');
INSERT INTO `sys_role_permission` VALUES ('30', '1', '18');
INSERT INTO `sys_role_permission` VALUES ('31', '1', '19');
INSERT INTO `sys_role_permission` VALUES ('32', '1', '20');
INSERT INTO `sys_role_permission` VALUES ('4', '4', '11');
INSERT INTO `sys_role_permission` VALUES ('5', '4', '12');
INSERT INTO `sys_role_permission` VALUES ('6', '3', '4');
INSERT INTO `sys_role_permission` VALUES ('7', '3', '6');
INSERT INTO `sys_role_permission` VALUES ('8', '3', '7');
INSERT INTO `sys_role_permission` VALUES ('9', '3', '13');

-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵ID',
  `nickname` varchar(20) DEFAULT NULL COMMENT '使用者暱稱',
  `email` varchar(128) DEFAULT NULL COMMENT '郵箱|登入帳號',
  `pswd` varchar(255) DEFAULT NULL COMMENT '密碼',
  `create_time` datetime DEFAULT NULL COMMENT '建立時間',
  `last_login_time` datetime DEFAULT NULL COMMENT '最後登入時間',
  `status` TINYINT DEFAULT '1' COMMENT '1:有效,0:禁止登入',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES ('1', 'admin', '
[email protected]
', '123456', NOW(), NOW(), '1'); INSERT INTO `sys_user` VALUES ('11', 'root', '[email protected]', '123456', '2016-05-26 20:50:54', '2017-02-13 15:49:04', '1'); INSERT INTO `sys_user` VALUES ('12', '8446666', '8446666', 'CpievEp3tWpuK7exnZldGFzkQJDBPimEt+zG1EbUth6pmRt2pMLwSxtNJEhBRJRU', '2016-05-27 22:34:19', '2016-06-15 17:03:16', '1'); INSERT INTO `sys_user` VALUES ('13', '123', '123', 'CpievEp3tWpuK7exnZldGFzkQJDBPimEt+zG1EbUth6pmRt2pMLwSxtNJEhBRJRU', '2016-05-27 22:34:19', '2016-06-15 17:03:16', '0'); INSERT INTO `sys_user` VALUES ('14', 'haiqin', '
[email protected]
', 'CpievEp3tWpuK7exnZldGFzkQJDBPimEt+zG1EbUth6pmRt2pMLwSxtNJEhBRJRU', '2016-05-27 22:34:19', '2017-03-23 21:39:44', '1'); -- ---------------------------- -- Table structure for sys_user_role -- ---------------------------- DROP TABLE IF EXISTS `sys_user_role`; CREATE TABLE `sys_user_role` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵ID', `uid` varchar(64) DEFAULT NULL COMMENT '使用者ID', `rid` varchar(64) DEFAULT NULL COMMENT '角色ID', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of sys_user_role -- ---------------------------- INSERT INTO `sys_user_role` VALUES ('1', '12', '4'); INSERT INTO `sys_user_role` VALUES ('2', '11', '3'); INSERT INTO `sys_user_role` VALUES ('3', '11', '4'); INSERT INTO `sys_user_role` VALUES ('4', '1', '1');

(二)shiro配置類:

package com.sun.configuration;

import org.apache.log4j.Logger;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Shiro 配置
 * Apache Shiro 核心通過 Filter 來實現,就好像SpringMvc 通過DispachServlet 來主控制一樣。
 * 既然是使用 Filter 一般也就能猜到,是通過URL規則來進行過濾和許可權校驗,所以我們需要定義一系列關於URL的規則和訪問許可權。
 * Created by sun on 2017-4-2.
 */
@Configuration
@EnableTransactionManagement
public class ShiroConfiguration{

    private final Logger logger = Logger.getLogger(ShiroConfiguration.class);

    /**
     * ShiroFilterFactoryBean 處理攔截資原始檔問題。
     * 注意:單獨一個ShiroFilterFactoryBean配置是或報錯的,因為在
     * 初始化ShiroFilterFactoryBean的時候需要注入:SecurityManager
     *
     Filter Chain定義說明
     1、一個URL可以配置多個Filter,使用逗號分隔
     2、當設定多個過濾器時,全部驗證通過,才視為通過
     3、部分過濾器可指定引數,如perms,roles
     *
     */
    @Bean
    public EhCacheManager getEhCacheManager(){
        EhCacheManager ehcacheManager = new EhCacheManager();
        ehcacheManager.setCacheManagerConfigFile("classpath:config/ehcache-shiro.xml");
        return ehcacheManager;
    }

    @Bean(name = "myShiroRealm")
    public MyShiroRealm myShiroRealm(EhCacheManager ehCacheManager){
        MyShiroRealm realm = new MyShiroRealm();
        realm.setCacheManager(ehCacheManager);
        return realm;
    }

    @Bean(name = "lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
        return new LifecycleBeanPostProcessor();
    }

    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
        creator.setProxyTargetClass(true);
        return creator;
    }

    @Bean(name = "securityManager")
    public DefaultWebSecurityManager defaultWebSecurityManager(MyShiroRealm realm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //設定realm
        securityManager.setRealm(realm);
        securityManager.setCacheManager(getEhCacheManager());
        return securityManager;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager){
        ShiroFilterFactoryBean factoryBean = new MyShiroFilterFactoryBean();
        factoryBean.setSecurityManager(securityManager);
        // 如果不設定預設會自動尋找Web工程根目錄下的"/login.jsp"頁面
        factoryBean.setLoginUrl("/login");
        // 登入成功後要跳轉的連線
        factoryBean.setSuccessUrl("/welcome");
        factoryBean.setUnauthorizedUrl("/403");
        loadShiroFilterChain(factoryBean);
        logger.info("shiro攔截器工廠類注入成功");
        return factoryBean;
    }

    /**
     * 載入ShiroFilter許可權控制規則
     */
    private void loadShiroFilterChain(ShiroFilterFactoryBean factoryBean) {
        /**下面這些規則配置最好配置到配置檔案中*/
        Map<String, String> filterChainMap = new LinkedHashMap<String, String>();
        /** authc:該過濾器下的頁面必須驗證後才能訪問,它是Shiro內建的一個攔截器
         * org.apache.shiro.web.filter.authc.FormAuthenticationFilter */
        // anon:它對應的過濾器裡面是空的,什麼都沒做,可以理解為不攔截
        //authc:所有url都必須認證通過才可以訪問; anon:所有url都都可以匿名訪問
        filterChainMap.put("/permission/userInsert", "anon");
        filterChainMap.put("/error", "anon");
        filterChainMap.put("/tUser/insert","anon");
        filterChainMap.put("/**", "authc");

        factoryBean.setFilterChainDefinitionMap(filterChainMap);
    }

    /*1.LifecycleBeanPostProcessor,這是個DestructionAwareBeanPostProcessor的子類,負責org.apache.shiro.util.Initializable型別bean的生命週期的,初始化和銷燬。主要是AuthorizingRealm類的子類,以及EhCacheManager類。
    2.HashedCredentialsMatcher,這個類是為了對密碼進行編碼的,防止密碼在資料庫裡明碼儲存,當然在登陸認證的生活,這個類也負責對form裡輸入的密碼進行編碼。
    3.ShiroRealm,這是個自定義的認證類,繼承自AuthorizingRealm,負責使用者的認證和許可權的處理,可以參考JdbcRealm的實現。
    4.EhCacheManager,快取管理,使用者登陸成功後,把使用者資訊和許可權資訊快取起來,然後每次使用者請求時,放入使用者的session中,如果不設定這個bean,每個請求都會查詢一次資料庫。
    5.SecurityManager,許可權管理,這個類組合了登陸,登出,許可權,session的處理,是個比較重要的類。
    6.ShiroFilterFactoryBean,是個factorybean,為了生成ShiroFilter。它主要保持了三項資料,securityManager,filters,filterChainDefinitionManager。
    7.DefaultAdvisorAutoProxyCreator,Spring的一個bean,由Advisor決定對哪些類的方法進行AOP代理。
    8.AuthorizationAttributeSourceAdvisor,shiro裡實現的Advisor類,內部使用AopAllianceAnnotationsAuthorizingMethodInterceptor來攔截用以下註解的方法。*/

}

package com.sun.configuration;

import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.mgt.FilterChainManager;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
import org.apache.shiro.web.mgt.WebSecurityManager;
import org.apache.shiro.web.servlet.AbstractShiroFilter;
import org.springframework.beans.factory.BeanInitializationException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

/**
 * Created by sun on 2017-4-2.
 */
public class MyShiroFilterFactoryBean extends ShiroFilterFactoryBean {
    // ShiroFilter將直接忽略的請求
    private Set<String> ignoreExt;

    public MyShiroFilterFactoryBean(){
        super();
        ignoreExt = new HashSet<String>();
        ignoreExt.add(".jpg");
        ignoreExt.add(".png");
        ignoreExt.add(".gif");
        ignoreExt.add(".bmp");
        ignoreExt.add(".js");
        ignoreExt.add(".css");
    }
    /**
     * 啟動時載入
     */
    @Override
    protected AbstractShiroFilter createInstance() throws Exception {
        SecurityManager securityManager = getSecurityManager();
        if (securityManager == null){
            throw new BeanInitializationException("SecurityManager property must be set.");
        }

        if (!(securityManager instanceof WebSecurityManager)){
            throw new BeanInitializationException("The security manager does not implement the WebSecurityManager interface.");
        }

        PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
        FilterChainManager chainManager = createFilterChainManager();
        chainResolver.setFilterChainManager(chainManager);
        return new MySpringShiroFilter((WebSecurityManager)securityManager, chainResolver);
    }

    /**
     * 啟動時載入
     */
    private class MySpringShiroFilter extends AbstractShiroFilter {
        public MySpringShiroFilter(
                WebSecurityManager securityManager, PathMatchingFilterChainResolver chainResolver) {
            super();
            if (securityManager == null){
                throw new IllegalArgumentException("WebSecurityManager property cannot be null.");
            }
            setSecurityManager(securityManager);
            if (chainResolver != null){
                setFilterChainResolver(chainResolver);
            }
        }
        /**
         * 頁面上傳輸的url先進入此方法驗證
         */
        @Override
        protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse,
                                        FilterChain chain)
                throws ServletException, IOException {
            HttpServletRequest request = (HttpServletRequest)servletRequest;
            String str = request.getRequestURI().toLowerCase();
            boolean flag = true;
            int idx = 0;
            if ((idx = str.lastIndexOf(".")) > 0){
                str = str.substring(idx);
                if (ignoreExt.contains(str.toLowerCase())){
                    flag = false;
                }
            }
            if (flag){
                super.doFilterInternal(servletRequest, servletResponse, chain);
            } else {
                chain.doFilter(servletRequest, servletResponse);
            }
        }
    }
}

package com.sun.configuration;

import com.sun.permission.model.Role;
import com.sun.permission.model.User;
import com.sun.permission.service.PermissionService;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.apache.log4j.Logger;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;

/**
 * shiro的認證最終是交給了Realm進行執行
 * 所以我們需要自己重新實現一個Realm,此Realm繼承AuthorizingRealm
 * Created by sun on 2017-4-2.
 */
public class MyShiroRealm extends AuthorizingRealm {

    private static final Logger logger = Logger.getLogger(MyShiroRealm.class);
    @Autowired
    private PermissionService permissionService;
    /**
     * 登入認證
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //UsernamePasswordToken用於存放提交的登入資訊
        UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
        logger.info("登入認證!");
        logger.info("驗證當前Subject時獲取到token為:" + ReflectionToStringBuilder.toString(token, ToStringStyle.MULTI_LINE_STYLE));
        User user = permissionService.findByUserEmail(token.getUsername());
        if (user != null){
            logger.info("使用者: " + user.getEmail());
            if(user.getStatus() == 0){
                throw new DisabledAccountException();
            }
            // 若存在,將此使用者存放到登入認證info中,無需自己做密碼對比,Shiro會為我們進行密碼對比校驗
            return new SimpleAuthenticationInfo(user.getEmail(), user.getPswd(), getName());
        }
        return null;
    }

    /**
     * 許可權認證(為當前登入的Subject授予角色和許可權)
     *
     * 該方法的呼叫時機為需授權資源被訪問時,並且每次訪問需授權資源都會執行該方法中的邏輯,這表明本例中並未啟用AuthorizationCache,
     * 如果連續訪問同一個URL(比如重新整理),該方法不會被重複呼叫,Shiro有一個時間間隔(也就是cache時間,在ehcache-shiro.xml中配置),
     * 超過這個時間間隔再重新整理頁面,該方法會被執行
     *
     * doGetAuthorizationInfo()是許可權控制,
     * 當訪問到頁面的時候,使用了相應的註解或者shiro標籤才會執行此方法否則不會執行,
     * 所以如果只是簡單的身份認證沒有許可權的控制的話,那麼這個方法可以不進行實現,直接返回null即可
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String loginName = (String) super.getAvailablePrincipal(principals);
        User user = permissionService.findByUserEmail(loginName);
        logger.info("許可權認證!");
        if (user != null){
            // 許可權資訊物件info,用來存放查出的使用者的所有的角色及許可權
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            //使用者的角色集合
            info.setRoles(permissionService.getRolesName(user.getId()));
            List<Role> roleList = permissionService.getRoleList(user.getId());
            for (Role role : roleList){
                //使用者的角色對應的所有許可權
                logger.info("角色: "+role.getName());
                info.addStringPermissions(permissionService.getPermissionsName(role.getId()));
            }
            return info;
        }
        // 返回null將會導致使用者訪問任何被攔截的請求時都會自動跳轉到unauthorizedUrl指定的地址
        return null;
    }
}

ehcache-shiro.xml內容:

<?xml version="1.0" encoding="UTF-8"?>
<ehcache updateCheck="false" name="shiroCache">
    <!--
       name:快取名稱。
       maxElementsInMemory:快取最大數目
       maxElementsOnDisk:硬碟最大快取個數。
       eternal:物件是否永久有效,一但設定了,timeout將不起作用。
       overflowToDisk:是否儲存到磁碟,當系統當機時
       timeToIdleSeconds:設定物件在失效前的允許閒置時間(單位:秒)。僅當eternal=false物件不是永久有效時使用,可選屬性,預設值是0,也就是可閒置時間無窮大。
       timeToLiveSeconds:設定物件在失效前允許存活時間(單位:秒)。最大時間介於建立時間和失效時間之間。僅當eternal=false物件不是永久有效時使用,預設是0.,也就是物件存活時間無窮大。
       diskPersistent:是否快取虛擬機器重啟期資料 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
       diskSpoolBufferSizeMB:這個引數設定DiskStore(磁碟快取)的快取區大小。預設是30MB。每個Cache都應該有自己的一個緩衝區。
       diskExpiryThreadIntervalSeconds:磁碟失效執行緒執行時間間隔,預設是120秒。
       memoryStoreEvictionPolicy:當達到maxElementsInMemory限制時,Ehcache將會根據指定的策略去清理記憶體。預設策略是LRU(最近最少使用)。你可以設定為FIFO(先進先出)或是LFU(較少使用)。
        clearOnFlush:記憶體數量最大時是否清除。
         memoryStoreEvictionPolicy:
            Ehcache的三種清空策略;
            FIFO,first in first out,這個是大家最熟的,先進先出。
            LFU, Less Frequently Used,就是上面例子中使用的策略,直白一點就是講一直以來最少被使用的。如上面所講,快取的元素有一個hit屬性,hit值最小的將會被清出快取。
            LRU,Least Recently Used,最近最少使用的,快取的元素有一個時間戳,當快取容量滿了,而又需要騰出地方來快取新的元素的時候,那麼現有快取元素中時間戳離當前時間最遠的元素將被清出快取。
    -->
    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="false"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"
    />
    <!-- 登入記錄快取鎖定10分鐘 -->
    <cache name="passwordRetryCache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="3600"
           timeToLiveSeconds="0"
           overflowToDisk="false"
           statistics="true">
    </cache>
</ehcache>
登入的controller類如下:
package com.sun.permission.controller;

import com.sun.permission.model.User;
import com.sun.permission.service.PermissionService;
import com.sun.util.CommonUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import javax.validation.Valid;

/**
 * Created by sun on 2017-4-2.
 */
@Controller
public class LoginController {
    private static final Logger logger = Logger.getLogger(LoginController.class);
    @Autowired
    private PermissionService permissionService;

    @RequestMapping(value="/login",method= RequestMethod.GET)
    public ModelAndView loginForm(){
        ModelAndView model = new ModelAndView();
        model.addObject("user", new User());
        model.setViewName("login");
        return model;
    }

    @RequestMapping(value="/login",method=RequestMethod.POST)
    public String login(@Valid User user, BindingResult bindingResult, RedirectAttributes redirectAttributes){
        if(bindingResult.hasErrors()){
            return "redirect:login";
        }
        String email = user.getEmail();
        if(StringUtils.isBlank(user.getEmail()) || StringUtils.isBlank(user.getPswd())){
            logger.info("使用者名稱或密碼為空! ");
            redirectAttributes.addFlashAttribute("message", "使用者名稱或密碼為空!");
            return "redirect:login";
        }
        //驗證
        UsernamePasswordToken token = new UsernamePasswordToken(user.getEmail(), user.getPswd());
        //獲取當前的Subject
        Subject currentUser = SecurityUtils.getSubject();
        try {
            //在呼叫了login方法後,SecurityManager會收到AuthenticationToken,並將其傳送給已配置的Realm執行必須的認證檢查
            //每個Realm都能在必要時對提交的AuthenticationTokens作出反應
            //所以這一步在呼叫login(token)方法時,它會走到MyRealm.doGetAuthenticationInfo()方法中,具體驗證方式詳見此方法
            logger.info("對使用者[" + email + "]進行登入驗證..驗證開始");
            currentUser.login(token);
            logger.info("對使用者[" + email + "]進行登入驗證..驗證通過");
        }catch(UnknownAccountException uae){
            logger.info("對使用者[" + email + "]進行登入驗證..驗證未通過,未知賬戶");
            redirectAttributes.addFlashAttribute("message", "未知賬戶");
        }catch(IncorrectCredentialsException ice){
            logger.info("對使用者[" + email + "]進行登入驗證..驗證未通過,錯誤的憑證");
            redirectAttributes.addFlashAttribute("message", "密碼不正確");
        }catch(LockedAccountException lae){
            logger.info("對使用者[" + email + "]進行登入驗證..驗證未通過,賬戶已鎖定");
            redirectAttributes.addFlashAttribute("message", "賬戶已鎖定");
        }catch(ExcessiveAttemptsException eae){
            logger.info("對使用者[" + email + "]進行登入驗證..驗證未通過,錯誤次數大於5次,賬戶已鎖定");
            redirectAttributes.addFlashAttribute("message", "使用者名稱或密碼錯誤次數大於5次,賬戶已鎖定");
        }catch (DisabledAccountException sae){
            logger.info("對使用者[" + email + "]進行登入驗證..驗證未通過,帳號已經禁止登入");
            redirectAttributes.addFlashAttribute("message", "帳號已經禁止登入");
        }catch(AuthenticationException ae){
            //通過處理Shiro的執行時AuthenticationException就可以控制使用者登入失敗或密碼錯誤時的情景
            logger.info("對使用者[" + email + "]進行登入驗證..驗證未通過,堆疊軌跡如下");
            ae.printStackTrace();
            redirectAttributes.addFlashAttribute("message", "使用者名稱或密碼不正確");
        }
        //驗證是否登入成功
        if(currentUser.isAuthenticated()){
            logger.info("使用者[" + email + "]登入認證通過(這裡可以進行一些認證通過後的一些系統引數初始化操作)");
            //把當前使用者放入session
            Session session = currentUser.getSession();
            User tUser = permissionService.findByUserEmail(email);
            session.setAttribute("currentUser",tUser);
            return "/welcome";
        }else{
            token.clear();
            return "redirect:login";
        }
    }

    @RequestMapping(value="/logout",method=RequestMethod.GET)
    public String logout(RedirectAttributes redirectAttributes ){
        //使用許可權管理工具進行使用者的退出,跳出登入,給出提示資訊
        SecurityUtils.getSubject().logout();
        redirectAttributes.addFlashAttribute("message", "您已安全退出");
        return "redirect:login";
    }

    @RequestMapping("/403")
    public String unauthorizedRole(){
        logger.info("------沒有許可權-------");
        return "errorPermission";
    }

}

login.jsp如下:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">
    
    <title>Login</title>
	<meta http-equiv="pragma" content="no-cache">
	<meta http-equiv="cache-control" content="no-cache">
	<meta http-equiv="expires" content="0">    
	<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
	<meta http-equiv="description" content="This is my page">
	<!--
	<link rel="stylesheet" type="text/css" href="styles.css">
	-->
	<script src="<%=basePath%>js/jquery-2.1.4/jquery.min.js"></script>
  </head>
  
  <body style="margin-left: 500px">
     <h1 style="margin-left: 30px">登入頁面----</h1>
     <form action="<%=basePath%>/login" method="post">
         使用者名稱 : <input type="text" name="email" id="email"/><br>
         密碼: <input type="password" name="pswd" id="pswd"/><br>
         <input style="margin-left: 100px" type="submit" value="登入"/><input style="left: 50px" onclick="register()" type="button" value="註冊"/>
     </form>
     <h1 style="color: red">${message }</h1>
  </body>
  <script type="text/javascript">

      function register(){
          location.href="<%=basePath%>permission/userInsert";
      }

  </script>
</html>
welcome.jsp如下:
<%--
  Created by IntelliJ IDEA.
  User: sun
  Date: 2017-4-2
  Time: 20:17
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%--<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>--%>
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<html>
<head>
    <title>歡迎!</title>
</head>
<body style="text-align: center">
    <h1>${message }</h1>
    <h1>使用者列表--<a href="${pageContext.request.contextPath }/logout">退出登入</a></h1>
    當前登入使用者: <shiro:principal/><br/>
    <shiro:authenticated>我已登入,但未記住<br/></shiro:authenticated>
    <shiro:user>我已登入,或已記住<br/></shiro:user>
    <shiro:guest>我是訪客<br/></shiro:guest>
    <shiro:hasAnyRoles name="manager,admin">manager or admin 角色使用者登入顯示此內容<br/></shiro:hasAnyRoles>
    <shiro:hasRole name="系統管理員">我是系統管理員<br/></shiro:hasRole>
    <shiro:hasRole name="會員">我是會員<br/></shiro:hasRole>
    <h2>許可權列表</h2>
    <shiro:hasPermission name="許可權列表">具有許可權列表許可權使用者顯示此內容<br/></shiro:hasPermission>
    <shiro:hasPermission name="使用者列表">具有使用者列表許可權使用者顯示此內容<br/></shiro:hasPermission>
    <shiro:hasPermission name="線上使用者">具有線上使用者許可權使用者顯示此內容<br/></shiro:hasPermission>
    <shiro:lacksPermission name="角色分配儲存">不具有角色分配儲存許可權的使用者顯示此內容 <br/></shiro:lacksPermission>

</body>
</html>

啟動後在頁面上輸入:http://localhost:8080/boot/會跳轉到login登入頁面:


登入後會跳轉到welcome頁面,該頁面使用了shiro標籤,會進行許可權認證:



相關推薦

SpringBoot學習整合shiro身份認證許可權認證,使用EhCache快取

專案下載地址:http://download.csdn.NET/detail/aqsunkai/9805821 (一)在pom.xml中新增依賴: <properties> <shiro.version>1.3.2</shiro.ve

SpringBoot學習整合shirorememberMe記住我後自動登入session失效解決辦法

專案下載地址:http://download.csdn.NET/detail/aqsunkai/9805821 定義一個攔截器,判斷使用者是通過記住我登入時,查詢資料庫後臺自動登入,同時把使用者放入session中。 配置攔截器也很簡單,Spring 為此提供了基礎類Web

SpringBoot學習整合shiro自動登入功能rememberMe記住我功能

首先在shiro配置類中注入rememberMe管理器 /** * cookie物件; * rememberMeCookie()方法是設定Cookie的生成模版,比如cookie的name,cookie的有效時間等等。 * @return */ @Bean public

SpringBoot學習整合Mybatis,使用HikariCP超高性能數據源

sta 掃描 sap url compile ack 項目結構 連接 style 一、添加pom依賴jar包: 1 <!--整合mybatis--> 2 <dependency> 3 <groupId>org.mybat

SpringBoot學習整合MyBatis,使用Druid連線池

專案下載地址:http://download.csdn.NET/detail/aqsunkai/9805821 (一)新增pom依賴: <!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/m

SpringBoot學習整合Redis

專案下載地址:http://download.csdn.NET/detail/aqsunkai/9805821 pom.xml新增對redis的依賴: <!-- https://mvnrepository.com/artifact/org.springframewo

2018年11月13日Java學習之關鍵字static類成員類方法,單例設計,類的成員之初始化塊

1.類變數(類屬性)由該類的所有例項共享 static 修飾的變數就是類變數,可以直接不建立物件訪問靜態成員,所有例項可以共同修改這個值 2.類方法 static修飾的方法可以用類名.方法名()訪問 在static方法內部只能訪問類的static屬性,不能訪問

springBoot整合Redis單機版叢集版

        Spring Boot中除了對常用的關係型資料庫提供了優秀的自動化支援之外,對於很多NoSQL資料庫一樣提供了自動化配置的支援,包括:Redis, MongoDB, Elasticsearch, Solr和Cassandra。        Redis是一個開

MIT 6.001SICP 2nd word版chm版共享下載

MIT 6.001:SICP 2nd (Structure and Interpretation of Computer Programs -Second edition)   Harold Abelson and Gerald Jay Sussman   這本

10 種機器學習演算法的要點附 Python R 程式碼

1. 監督式學習 監督式學習演算法包括一個目標變數(因變數)和用來預測目標變數的預測變數(自變數)。通過這些變數我們可以搭建一個模型,從而對於一個已知的預測變數值,我們可以得到對應的目標變數值。重複訓練這個模型,直到它能在訓練資料集上達到預定的準確度。 屬於監

深度學習之---複雜性分析引數量計算次數

卷積神經網路的複雜度分析 轉行修行中 在梳理CNN經典模型的過程中,我理解到其實經典模型演進中的很多創新點都與改善模型計算複雜度緊密相關,因此今天就讓我們對卷積神經網路的複雜度分析簡單總結一下下。 本文主要關注的是針對模型本身的複雜度分析(其實並不是很複雜啦~)

Spring Boot2整合Shiro1身份認證

Spring Boot2整合Shiro(1):身份認證   前言 本文主要介紹了在Spring Boot2專案中整合Shiro實現登入認證。本文假設讀者已經對Shiro和基於RBAC的許可權控制系統有了基本的認識。  本專案沒有資料庫,也就沒有dao層,所有的使用者和

SpringBoot+shiro整合學習之登入認證許可權控制

學習任務目標 使用者必須要登陸之後才能訪問定義連結,否則跳轉到登入頁面。 對連結進行許可權控制,只有噹噹前登入使用者有這個連結訪問許可權才可以訪問,否則跳轉到指定頁面。 輸入錯誤密碼使用者名稱或則使用者被設定為靜止登入,返回相應json串資訊 匯

Spring Cloud之路SpringBoot+Shiro實現登入認證許可權管理

一、Shiro介紹 1、Shiro是什麼? Shiro是Apache下的一個開源專案,我們稱之為Apache Shiro。它是一個很易用與Java專案的的安全框架,提供了認證、授權、加密、會話管理,與 Spring Security 一樣都是做一個許可權的安全框架,但是與Spri

SpringBoot學習二十五SpringBoot整合Shiro詳細版本

整合內容包括 自定義realm,實現認證和授權 自定義加密,實現密碼加密驗證 自定義Cachemanager、Cache,實現Shiro的cache管理,儲存在redis中 自定義SessionManager、SessionDao、SessionIdCook

springboot(十四)springboot整合shiro-登入認證許可權管理

這篇文章我們來學習如何使用Spring Boot整合Apache Shiro。安全應該是網際網路公司的一道生命線,幾乎任何的公司都會涉及到這方面的需求。在Java領域一般有Spring Security、Apache Shiro等安全框架,但是由於Spring Sec

機器學習整合學習ensemble learning——原理概述

整合學習(ensemble learning) 整合學習通過構建多個個體學習器,然後再用某種策略將他們結合起來,產生一個有較好效果的強學習器來完成任務。基本原理如下圖所示。這裡主要有三個關鍵點:個體學習器的同質異質、個體學習器的生成方式、結合策略。 同質異質。首先需要明確的是個體學習器至少不差於弱學習器。

springboot 整合shiroehcache快取

1      匯入maven座標 <!--shiro start--> <dependency> <groupId>org.apache.shiro</groupId>

SpringBoot整合shiro基礎配置

公司專案採用的spring-boot框架。在做使用者許可權功能的時候準備採用shiro許可權框架。前面也考慮過spring家族的spring security安全框架。但是經過網上查詢對比最終選擇了shiro。因為shiro含有基本的安全控制功能,並且配置更為簡

機器學習整合學習ensemble),bootstrap,Bagging,隨機森林,Boosting

文章目錄 整合學習的樸素思想 Bootstrap理論 Bagging 隨機森林 Boosting 整合學習的樸素思想 整合學習基於這樣的思想:對於比較複雜的任務,綜合許多人的意見來進行決策會比“一家獨大”要更好。換句話說、就