Spring Boot 整合 Shiro

安全應(yīng)該是互聯(lián)網(wǎng)公司的一道生命線鲸阔,幾乎任何的公司都會(huì)涉及到這方面的需求疯趟。在Java領(lǐng)域一般有Spring Security晦譬、Apache Shiro等安全框架,但是由于Spring Security過(guò)于龐大和復(fù)雜习勤,大多數(shù)公司會(huì)選擇Apache Shiro來(lái)使用踪栋,這篇文章會(huì)先介紹一下Apache Shiro,在結(jié)合Spring Boot給出使用案例图毕。

Apache Shiro簡(jiǎn)介

Apache Shiro是一個(gè)功能強(qiáng)大夷都、靈活的,開(kāi)源的安全框架予颤。它可以干凈利落地處理身份驗(yàn)證损肛、授權(quán)、企業(yè)會(huì)話管理和加密荣瑟。

Shiro的主要功能

  • 驗(yàn)證用戶身份
  • 用戶訪問(wèn)權(quán)限控制治拿,比如:判斷用戶是否分配了一定的安全角色、判斷用戶是否被授予完成某個(gè)操作的權(quán)限
  • 在非 web 或 EJB 容器的環(huán)境下可以任意使用Session API
  • 可以響應(yīng)認(rèn)證笆焰、訪問(wèn)控制劫谅,或者 Session 生命周期中發(fā)生的事件
  • 可將一個(gè)或以上用戶安全數(shù)據(jù)源數(shù)據(jù)組合成一個(gè)復(fù)合的用戶 “view”(視圖)
  • 支持單點(diǎn)登錄(SSO)功能
  • 支持提供“Remember Me”服務(wù),獲取用戶關(guān)聯(lián)信息而無(wú)需登錄

Apache Shiro Features 特性

Apache Shiro是一個(gè)全面的、蘊(yùn)含豐富功能的安全框架捏检。Shiro的功能框架圖:


image

Authentication(認(rèn)證), Authorization(授權(quán)), Session Management(會(huì)話管理), Cryptography(加密)被 Shiro 框架的開(kāi)發(fā)團(tuán)隊(duì)稱之為應(yīng)用安全的四大基石荞驴。

  • Authentication:用戶身份識(shí)別,通常被稱為用戶“登錄”
  • Authorization:訪問(wèn)控制贯城。比如某個(gè)用戶是否具有某個(gè)操作的使用權(quán)限熊楼。
  • Session Management:特定于用戶的會(huì)話管理,甚至在非web 或 EJB 應(yīng)用程序。
  • Cryptography:在對(duì)數(shù)據(jù)源使用加密算法加密的同時(shí)能犯,保證易于使用鲫骗。

還有其他的功能來(lái)支持和加強(qiáng)這些不同應(yīng)用環(huán)境下安全領(lǐng)域的關(guān)注點(diǎn)。特別是對(duì)以下的功能支持:

  • Web支持:Shiro 提供的 web 支持 api 踩晶,可以很輕松的保護(hù) web 應(yīng)用程序的安全执泰。
  • 緩存:緩存是 Apache Shiro 保證安全操作快速、高效的重要手段渡蜻。
  • 并發(fā):Apache Shiro 支持多線程應(yīng)用程序的并發(fā)特性术吝。
  • 測(cè)試:支持單元測(cè)試和集成測(cè)試,確保代碼和預(yù)想的一樣安全茸苇。
  • “Run As”:這個(gè)功能允許用戶假設(shè)另一個(gè)用戶的身份(在許可的前提下)排苍。
  • “Remember Me”:跨 session 記錄用戶的身份,只有在強(qiáng)制需要時(shí)才需要登錄学密。

注意: Shiro不會(huì)去維護(hù)用戶纪岁、維護(hù)權(quán)限,這些需要我們自己去設(shè)計(jì)/提供则果,然后通過(guò)相應(yīng)的接口注入給Shiro。

High-Level Overview 高級(jí)概述

在概念層漩氨,Shiro 架構(gòu)包含三個(gè)主要的理念:Subject西壮、SecurityManager和 Realm。下圖展示了這些組件如何相互作用:


image
  • Subject:當(dāng)前用戶叫惊,Subject 可以是一個(gè)人款青,但也可以是第三方服務(wù)、守護(hù)進(jìn)程帳戶霍狰、時(shí)鐘守護(hù)任務(wù)或者其它–當(dāng)前和軟件交互的任何事件抡草。
  • SecurityManager:管理所有Subject,SecurityManager 是 Shiro 架構(gòu)的核心蔗坯,配合內(nèi)部安全組件共同組成安全傘康震。
  • Realms:用于進(jìn)行權(quán)限信息的驗(yàn)證,我們自己實(shí)現(xiàn)宾濒。Realm 本質(zhì)上是一個(gè)特定的安全 DAO:它封裝與數(shù)據(jù)源連接的細(xì)節(jié)腿短,得到Shiro 所需的相關(guān)的數(shù)據(jù)。在配置 Shiro 的時(shí)候,你必須指定至少一個(gè)Realm 來(lái)實(shí)現(xiàn)認(rèn)證(authentication)和/或授權(quán)(authorization)橘忱。

我們需要實(shí)現(xiàn)Realms的Authentication 和 Authorization赴魁。其中 Authentication 是用來(lái)驗(yàn)證用戶身份,Authorization 是授權(quán)訪問(wèn)控制钝诚,用于對(duì)用戶進(jìn)行的操作授權(quán)颖御,證明該用戶是否允許進(jìn)行當(dāng)前操作,如訪問(wèn)某個(gè)鏈接凝颇,某個(gè)資源文件等潘拱。

構(gòu)建項(xiàng)目

添加shiro-spring依賴*

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.4.0</version>
</dependency>

配置文件

spring:
    datasource:
      url: jdbc:mysql://localhost:3306/login
      username: root
      password: 123456
      #schema: database/import.sql
      #sql-script-encoding: utf-8
      driver-class-name: com.mysql.jdbc.Driver

    jpa:
      database: mysql
      show-sql: true
      hibernate:
        ddl-auto: update
        naming:
          strategy: org.hibernate.cfg.DefaultComponentSafeNamingStrategy
      properties:
         hibernate:
            dialect: org.hibernate.dialect.MySQL5Dialect

    thymeleaf:
       cache: false
       mode: LEGACYHTML5

RBAC

RBAC 是基于角色的訪問(wèn)控制(Role-Based Access Control )在 RBAC 中,權(quán)限與角色相關(guān)聯(lián)祈噪,用戶通過(guò)成為適當(dāng)角色的成員而得到這些角色的權(quán)限泽铛。這就極大地簡(jiǎn)化了權(quán)限的管理。這樣管理都是層級(jí)相互依賴的辑鲤,權(quán)限賦予給角色盔腔,而把角色又賦予用戶,這樣的權(quán)限設(shè)計(jì)很清楚月褥,管理起來(lái)很方便弛随。

用戶信息

@Entity
public class UserInfo implements Serializable {
    @Id
    @GeneratedValue
    private Integer uid;
    @Column(unique =true)
    private String username;//帳號(hào)
    private String name;//名稱(昵稱或者真實(shí)姓名,不同系統(tǒng)不同定義)
    private String password; //密碼;
    private String salt;//加密密碼的鹽
    private byte state;//用戶狀態(tài),0:創(chuàng)建未認(rèn)證(比如沒(méi)有激活宁赤,沒(méi)有輸入驗(yàn)證碼等等)--等待驗(yàn)證的用戶 , 1:正常狀態(tài),2:用戶被鎖定.
    @ManyToMany(fetch= FetchType.EAGER)//立即從數(shù)據(jù)庫(kù)中進(jìn)行加載數(shù)據(jù);
    @JoinTable(name = "SysUserRole", joinColumns = { @JoinColumn(name = "uid") }, inverseJoinColumns ={@JoinColumn(name = "roleId") })
    private List<SysRole> roleList;// 一個(gè)用戶具有多個(gè)角色
     /**
     * 密碼鹽舀透,重新對(duì)鹽重新進(jìn)行了定義,用戶名+salt决左,這樣就更加不容易被破解
     */
    public String getCredentialsSalt(){
        return this.username+this.salt;
    }
    ...省略set/get方法

角色信息

@Entity
public class SysRole {
    @Id@GeneratedValue
    private Integer id; // 編號(hào)
    private String role; // 角色標(biāo)識(shí)程序中判斷使用,如"admin",這個(gè)是唯一的:
    private String description; // 角色描述,UI界面顯示使用
    private Boolean available = Boolean.FALSE; // 是否可用,如果不可用將不會(huì)添加給用戶

    //角色 -- 權(quán)限關(guān)系:多對(duì)多關(guān)系;
    @ManyToMany(fetch= FetchType.EAGER)
    @JoinTable(name="SysRolePermission",joinColumns={@JoinColumn(name="roleId")},inverseJoinColumns={@JoinColumn(name="permissionId")})
    private List<SysPermission> permissions;

    // 用戶 - 角色關(guān)系定義;
    @ManyToMany
    @JoinTable(name="SysUserRole",joinColumns={@JoinColumn(name="roleId")},inverseJoinColumns={@JoinColumn(name="uid")})
    private List<UserInfo> userInfos;// 一個(gè)角色對(duì)應(yīng)多個(gè)用戶

權(quán)限信息

@Entity
public class SysPermission implements Serializable {
    @Id@GeneratedValue
    private Integer id;//主鍵.
    private String name;//名稱.
    @Column(columnDefinition="enum('menu','button')")
    private String resourceType;//資源類型愕够,[menu|button]
    private String url;//資源路徑.
    private String permission; //權(quán)限字符串,menu例子:role:*,button例子:role:create,role:update,role:delete,role:view
    private Long parentId; //父編號(hào)
    private String parentIds; //父編號(hào)列表
    private Boolean available = Boolean.FALSE;
    @ManyToMany
    @JoinTable(name="SysRolePermission",joinColumns={@JoinColumn(name="permissionId")},inverseJoinColumns={@JoinColumn(name="roleId")})
    private List<SysRole> roles;

根據(jù)以上的代碼會(huì)自動(dòng)生成user_info(用戶信息表)佛猛、sys_role(角色表)惑芭、sys_permission(權(quán)限表)、sys_user_role(用戶角色表)继找、sys_role_permission(角色權(quán)限表)這五張表遂跟,為了方便測(cè)試我們給這五張表插入一些初始化數(shù)據(jù):

INSERT INTO `user_info` (`uid`,`username`,`name`,`password`,`salt`,`state`) VALUES ('1', 'admin', '管理員', 'd3c59d25033dbf980d29554025c23a75', '8d78869f470951332959580424d4bf4f', 0);
INSERT INTO `sys_permission` (`id`,`available`,`name`,`parent_id`,`parent_ids`,`permission`,`resource_type`,`url`) VALUES (1,0,'用戶管理',0,'0/','userInfo:view','menu','userInfo/userList');
INSERT INTO `sys_permission` (`id`,`available`,`name`,`parent_id`,`parent_ids`,`permission`,`resource_type`,`url`) VALUES (2,0,'用戶添加',1,'0/1','userInfo:add','button','userInfo/userAdd');
INSERT INTO `sys_permission` (`id`,`available`,`name`,`parent_id`,`parent_ids`,`permission`,`resource_type`,`url`) VALUES (3,0,'用戶刪除',1,'0/1','userInfo:del','button','userInfo/userDel');
INSERT INTO `sys_role` (`id`,`available`,`description`,`role`) VALUES (1,0,'管理員','admin');
INSERT INTO `sys_role` (`id`,`available`,`description`,`role`) VALUES (2,0,'VIP會(huì)員','vip');
INSERT INTO `sys_role` (`id`,`available`,`description`,`role`) VALUES (3,1,'test','test');
INSERT INTO `sys_role_permission` VALUES ('1', '1');
INSERT INTO `sys_role_permission` (`permission_id`,`role_id`) VALUES (1,1);
INSERT INTO `sys_role_permission` (`permission_id`,`role_id`) VALUES (2,1);
INSERT INTO `sys_role_permission` (`permission_id`,`role_id`) VALUES (3,2);
INSERT INTO `sys_user_role` (`role_id`,`uid`) VALUES (1,1);

Shiro 配置

首先要配置的是ShiroConfig類,Apache Shiro 核心通過(guò) Filter 來(lái)實(shí)現(xiàn)婴渡,就好像SpringMvc 通過(guò)DispachServlet 來(lái)主控制一樣幻锁。 既然是使用 Filter 一般也就能猜到,是通過(guò)URL規(guī)則來(lái)進(jìn)行過(guò)濾和權(quán)限校驗(yàn)边臼,所以我們需要定義一系列關(guān)于URL的規(guī)則和訪問(wèn)權(quán)限哄尔。

ShiroConfig

@Configuration
public class ShiroConfig {
    @Bean
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
        System.out.println("ShiroConfiguration.shirFilter()");
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //攔截器.
        Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();
        // 配置不會(huì)被攔截的鏈接 順序判斷
        filterChainDefinitionMap.put("/static/**", "anon");
        //配置退出 過(guò)濾器,其中的具體的退出代碼Shiro已經(jīng)替我們實(shí)現(xiàn)了
        filterChainDefinitionMap.put("/logout", "logout");
        //<!-- 過(guò)濾鏈定義,從上向下順序執(zhí)行柠并,一般將/**放在最為下邊 -->:這是一個(gè)坑呢究飞,一不小心代碼就不好使了;
        //<!-- authc:所有url都必須認(rèn)證通過(guò)才可以訪問(wèn); anon:所有url都都可以匿名訪問(wèn)-->
        filterChainDefinitionMap.put("/**", "authc");
        // 如果不設(shè)置默認(rèn)會(huì)自動(dòng)尋找Web工程根目錄下的"/login.jsp"頁(yè)面
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 登錄成功后要跳轉(zhuǎn)的鏈接
        shiroFilterFactoryBean.setSuccessUrl("/index");

        //未授權(quán)界面;
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }
    /**
     * 憑證匹配器(由于我們的密碼校驗(yàn)交給Shiro的SimpleAuthenticationInfo進(jìn)行處理了 )
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher(){
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:這里使用MD5算法;
        hashedCredentialsMatcher.setHashIterations(2);//散列的次數(shù)置谦,比如散列兩次,相當(dāng)于 md5(md5(""));
        return hashedCredentialsMatcher;
    }

    @Bean
    public MyShiroRealm myShiroRealm(){
        MyShiroRealm myShiroRealm = new MyShiroRealm();
        myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return myShiroRealm;
    }

    @Bean
    public SecurityManager securityManager(){
        DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
        securityManager.setRealm(myShiroRealm());
        return securityManager;
    }
    /**
     *  開(kāi)啟shiro aop注解支持.
     *  使用代理方式;所以需要開(kāi)啟代碼支持;
     * @param securityManager
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    @Bean(name="simpleMappingExceptionResolver")
    public SimpleMappingExceptionResolver
    createSimpleMappingExceptionResolver() {
        SimpleMappingExceptionResolver r = new SimpleMappingExceptionResolver();
        Properties mappings = new Properties();
        mappings.setProperty("DatabaseException", "databaseError");//數(shù)據(jù)庫(kù)異常處理
        mappings.setProperty("UnauthorizedException","403");
        r.setExceptionMappings(mappings);  // None by default
        r.setDefaultErrorView("error");    // No default
        r.setExceptionAttribute("ex");     // Default is "exception"
        //r.setWarnLogCategory("example.MvcLogger");     // No default
        return r;
    }
}

Filter Chain定義說(shuō)明:

  • 1亿傅、一個(gè)URL可以配置多個(gè)Filter媒峡,使用逗號(hào)分隔
  • 2、當(dāng)設(shè)置多個(gè)過(guò)濾器時(shí)葵擎,全部驗(yàn)證通過(guò)谅阿,才視為通過(guò)
  • 3、部分過(guò)濾器可指定參數(shù)酬滤,如perms签餐,roles

Shiro內(nèi)置的FilterChain

  • anon:所有url都都可以匿名訪問(wèn)
  • authc: 需要認(rèn)證才能進(jìn)行訪問(wèn)
  • user:配置記住我或認(rèn)證通過(guò)可以訪問(wèn)
    ......

登錄認(rèn)證實(shí)現(xiàn)

在認(rèn)證、授權(quán)內(nèi)部實(shí)現(xiàn)機(jī)制中都有提到盯串,最終處理都將交給Real進(jìn)行處理氯檐。因?yàn)樵赟hiro中,最終是通過(guò)Realm來(lái)獲取應(yīng)用程序中的用戶体捏、角色及權(quán)限信息的冠摄。通常情況下,在Realm中會(huì)直接從我們的數(shù)據(jù)源中獲取Shiro需要的驗(yàn)證信息几缭『佑荆可以說(shuō),Realm是專用于安全框架的DAO. Shiro的認(rèn)證過(guò)程最終會(huì)交由Realm執(zhí)行年栓,這時(shí)會(huì)調(diào)用Realm的getAuthenticationInfo(token)方法拆挥。

該方法主要執(zhí)行以下操作:

  • 1、檢查提交的進(jìn)行認(rèn)證的令牌信息
  • 2某抓、根據(jù)令牌信息從數(shù)據(jù)源(通常為數(shù)據(jù)庫(kù))中獲取用戶信息
  • 3纸兔、對(duì)用戶信息進(jìn)行匹配驗(yàn)證。
  • 4否副、驗(yàn)證通過(guò)將返回一個(gè)封裝了用戶信息的AuthenticationInfo實(shí)例汉矿。
  • 5、驗(yàn)證失敗則拋出AuthenticationException異常信息副编。
    而在我們的應(yīng)用程序中要做的就是自定義一個(gè)Realm類,繼承AuthorizingRealm抽象類流强,重載doGetAuthenticationInfo()痹届,重寫(xiě)獲取用戶信息的方法。

鏈接權(quán)限的實(shí)現(xiàn)

shiro的權(quán)限授權(quán)是通過(guò)繼承AuthorizingRealm抽象類打月,重載doGetAuthorizationInfo();當(dāng)訪問(wèn)到頁(yè)面的時(shí)候队腐,鏈接配置了相應(yīng)的權(quán)限或者shiro標(biāo)簽才會(huì)執(zhí)行此方法否則不會(huì)執(zhí)行,所以如果只是簡(jiǎn)單的身份認(rèn)證沒(méi)有權(quán)限的控制的話奏篙,那么這個(gè)方法可以不進(jìn)行實(shí)現(xiàn)柴淘,直接返回null即可迫淹。在這個(gè)方法中主要是使用類:SimpleAuthorizationInfo進(jìn)行角色的添加和權(quán)限的添加。

public class MyShiroRealm extends AuthorizingRealm {
    @Resource
    private UserInfoService userInfoService;
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("權(quán)限配置-->MyShiroRealm.doGetAuthorizationInfo()");
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        UserInfo userInfo  = (UserInfo)principals.getPrimaryPrincipal();
        for(SysRole role:userInfo.getRoleList()){
            authorizationInfo.addRole(role.getRole());
            for(SysPermission p:role.getPermissions()){
                authorizationInfo.addStringPermission(p.getPermission());
            }
        }
        return authorizationInfo;
    }

doGetAuthenticationInfo的重寫(xiě)

    //主要是用來(lái)進(jìn)行身份認(rèn)證的为严,也就是說(shuō)驗(yàn)證用戶輸入的賬號(hào)和密碼是否正確敛熬。
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
            throws AuthenticationException {
        System.out.println("MyShiroRealm.doGetAuthenticationInfo()");
        //獲取用戶的輸入的賬號(hào).
        String username = (String)token.getPrincipal();
        System.out.println(token.getCredentials());
        //通過(guò)username從數(shù)據(jù)庫(kù)中查找 User對(duì)象,如果找到第股,沒(méi)找到.
        //實(shí)際項(xiàng)目中应民,這里可以根據(jù)實(shí)際情況做緩存,如果不做夕吻,Shiro自己也是有時(shí)間間隔機(jī)制诲锹,2分鐘內(nèi)不會(huì)重復(fù)執(zhí)行該方法
        UserInfo userInfo = userInfoService.findByUsername(username);
        System.out.println("----->>userInfo="+userInfo);
        if(userInfo == null){
            return null;
        }
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                userInfo, //用戶名
                userInfo.getPassword(), //密碼
                ByteSource.Util.bytes(userInfo.getCredentialsSalt()),//salt=username+salt
                getName()  //realm name
        );
        return authenticationInfo;
    }
}

當(dāng)然也可以添加set集合:roles是從數(shù)據(jù)庫(kù)查詢的當(dāng)前用戶的角色,stringPermissions是從數(shù)據(jù)庫(kù)查詢的當(dāng)前用戶對(duì)應(yīng)的權(quán)限

authorizationInfo.setRoles(roles);
authorizationInfo.setStringPermissions(stringPermissions);

就是說(shuō)如果在shiro配置文件中添加了filterChainDefinitionMap.put(“/add”, “perms[權(quán)限添加]”);就說(shuō)明訪問(wèn)/add這個(gè)鏈接必須要有“權(quán)限添加”這個(gè)權(quán)限才可以訪問(wèn)涉馅,如果在shiro配置文件中添加了filterChainDefinitionMap.put(“/add”, “roles[100002]归园,perms[權(quán)限添加]”);就說(shuō)明訪問(wèn)/add這個(gè)鏈接必須要有“權(quán)限添加”這個(gè)權(quán)限和具有“100002”這個(gè)角色才可以訪問(wèn)。

登錄實(shí)現(xiàn)

登錄過(guò)程其實(shí)只是處理異常的相關(guān)信息稚矿,具體的登錄驗(yàn)證交給shiro來(lái)處理

@Controller
public class HomeController {
    @RequestMapping({"/","/index"})
    public String index(){
        return"/index";
    }

    @RequestMapping("/login")
    public String login(HttpServletRequest request, Map<String, Object> map) throws Exception{
        System.out.println("HomeController.login()");
        // 登錄失敗從request中獲取shiro處理的異常信息庸诱。
        // shiroLoginFailure:就是shiro異常類的全類名.
        String exception = (String) request.getAttribute("shiroLoginFailure");
        System.out.println("exception=" + exception);
        String msg = "";
        if (exception != null) {
            if (UnknownAccountException.class.getName().equals(exception)) {
                System.out.println("UnknownAccountException -- > 賬號(hào)不存在:");
                msg = "UnknownAccountException -- > 賬號(hào)不存在:";
            } else if (IncorrectCredentialsException.class.getName().equals(exception)) {
                System.out.println("IncorrectCredentialsException -- > 密碼不正確:");
                msg = "IncorrectCredentialsException -- > 密碼不正確:";
            } else if ("kaptchaValidateFailed".equals(exception)) {
                System.out.println("kaptchaValidateFailed -- > 驗(yàn)證碼錯(cuò)誤");
                msg = "kaptchaValidateFailed -- > 驗(yàn)證碼錯(cuò)誤";
            } else {
                msg = "else >> "+exception;
                System.out.println("else -- >" + exception);
            }
        }
        map.put("msg", msg);
        // 此方法不處理登錄成功,由shiro進(jìn)行處理
        return "/login";
    }

    @RequestMapping("/403")
    public String unauthorizedRole(){
        System.out.println("------沒(méi)有權(quán)限-------");
        return "403";
    }
}

dao層

public interface UserInfoDao extends CrudRepository<UserInfo,Long> {
    /**通過(guò)username查找用戶信息;*/
    public UserInfo findByUsername(String username);
}

service層

public interface UserInfoService {
    /**通過(guò)username查找用戶信息;*/
    public UserInfo findByUsername(String username);
}
@Service
public class UserInfoServiceImpl implements UserInfoService {
    @Resource
    private UserInfoDao userInfoDao;
    @Override
    public UserInfo findByUsername(String username) {
        System.out.println("UserInfoServiceImpl.findByUsername()");
        return userInfoDao.findByUsername(username);
    }
}

測(cè)試

1、編寫(xiě)好后就可以啟動(dòng)程序盐捷,訪問(wèn)http://localhost:8080/userInfo/userList頁(yè)面偶翅,由于沒(méi)有登錄就會(huì)跳轉(zhuǎn)到http://localhost:8080/login頁(yè)面。登錄之后就會(huì)跳轉(zhuǎn)到index頁(yè)面碉渡,登錄后聚谁,直接在瀏覽器中輸入http://localhost:8080/userInfo/userList訪問(wèn)就會(huì)看到用戶信息。上面這些操作時(shí)候觸發(fā)MyShiroRealm.doGetAuthenticationInfo()這個(gè)方法滞诺,也就是登錄認(rèn)證的方法形导。

2、登錄admin賬戶习霹,訪問(wèn):http://127.0.0.1:8080/userInfo/userAdd顯示用戶添加界面朵耕,訪問(wèn)http://127.0.0.1:8080/userInfo/userDel顯示403沒(méi)有權(quán)限。上面這些操作時(shí)候觸發(fā)MyShiroRealm.doGetAuthorizationInfo()這個(gè)方面淋叶,也就是權(quán)限校驗(yàn)的方法阎曹。

3、修改admin不同的權(quán)限進(jìn)行測(cè)試

shiro很強(qiáng)大煞檩,這僅僅是完成了登錄認(rèn)證和權(quán)限管理這兩個(gè)功能处嫌,更多內(nèi)容以后有時(shí)間再做探討。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末斟湃,一起剝皮案震驚了整個(gè)濱河市熏迹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌凝赛,老刑警劉巖注暗,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件坛缕,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡捆昏,警方通過(guò)查閱死者的電腦和手機(jī)赚楚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)屡立,“玉大人直晨,你說(shuō)我怎么就攤上這事∨蚶” “怎么了勇皇?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)焚刺。 經(jīng)常有香客問(wèn)我敛摘,道長(zhǎng),這世上最難降的妖魔是什么乳愉? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任兄淫,我火速辦了婚禮,結(jié)果婚禮上蔓姚,老公的妹妹穿的比我還像新娘捕虽。我一直安慰自己,他們只是感情好坡脐,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布泄私。 她就那樣靜靜地躺著,像睡著了一般备闲。 火紅的嫁衣襯著肌膚如雪晌端。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,370評(píng)論 1 302
  • 那天恬砂,我揣著相機(jī)與錄音咧纠,去河邊找鬼。 笑死泻骤,一個(gè)胖子當(dāng)著我的面吹牛漆羔,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播狱掂,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼演痒,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了符欠?” 一聲冷哼從身側(cè)響起嫡霞,我...
    開(kāi)封第一講書(shū)人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤瓶埋,失蹤者是張志新(化名)和其女友劉穎希柿,沒(méi)想到半個(gè)月后诊沪,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡妹蔽,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年秋泳,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了浓体。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡渐裸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出装悲,到底是詐尸還是另有隱情昏鹃,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布诀诊,位于F島的核電站洞渤,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏属瓣。R本人自食惡果不足惜载迄,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望抡蛙。 院中可真熱鬧护昧,春花似錦、人聲如沸粗截。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)慈格。三九已至怠晴,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間浴捆,已是汗流浹背蒜田。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留选泻,地道東北人冲粤。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像页眯,于是被迫代替她去往敵國(guó)和親梯捕。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容