Shiro集成SpringMVC工程整合

Apache Shiro Framework 介绍

第一章:Shiro简介
1、1 Shiro框架简介

Apache Shiro是Java的一个安全框架,旨在简化身份验证和授权。Shiro在JavaSE和JavaEE项目中都可以使用。它主要用来处理身份认证,授权,企业会话管理和加密等。

官网地址如下:http://shiro.apache.org/

Shiro的具体功能点如下:![屏幕快照 2017-08-25 下午4.24.25](屏幕快照 2017-08-25 下午4.24.25.png)

(1)身份认证/登录,验证用户是不是拥有相应的身份;
(2)授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;
(3)会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境的,也可以是如Web环境的;
(4)加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
(5)Web支持,可以非常容易的集成到Web环境;

(6) Caching :缓存,存储用户登录之后的信息,拥有角色和权限不需要每次都去查

##### 1、2 Shiro主要有四个组件

  1. SecurityManager典型的 Facade,Shiro 通过它对外提供安全管理的各种服务。

  2. Authenticator对“Who are you ?”进行核实。通常涉及用户名和密码。这 个组件负责收集 principals 和 credentials,并将它们提交给应用系统。如果提交的 credentials 跟应用系统中提供的 credentials 吻合,就能够继续访问,否则需要重新提交 principals 和 credentials,或者直接终止访问。

  3. Authorizer身 份份验证通过后,由这个组件对登录人员进行访问控制的筛查,比如“who can do what”, 或者“who can do which actions”。Shiro 采用“基于 Realm”的方法,即用户(又称 Subject)、用户组、角色和 permission 的聚合体。

  4. Session Manager这个组件保证了异构客户端的访问,配置简单。它是基于 POJO/J2SE 的,不跟任何的客户端或者协议绑定。

    20160809110821940

同样的虚线框框圈着的是Shiro3大核心组件:

Subject :正与系统进行交互的人,或某一个第三方服务。所有 Subject 实例都被绑定到(且这是必须的)一个SecurityManager 上。
SecurityManager:Shiro 架构的心脏,用来协调内部各安全组件,管理内部组件实例,并通过它来提供安全管理的各种服务。当 Shiro 与一个 Subject 进行交互时,实质上是幕后的 SecurityManager 处理所有繁重的 Subject 安全操作。
Realms :本质上是一个特定安全的 DAO。当配置 Shiro 时,必须指定至少一个 Realm 用来进行身份验证和/或授权。Shiro 提供了多种可用的 Realms 来获取安全相关的数据。如关系数据库(JDBC),INI 及属性文件等。可以定义自己 Realm 实现来代表自定义的数据源。

第二章:Shiro之编码加密

pom.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.sudojava.shiro</groupId>
    <artifactId>shiro_java</artifactId>
    <version>1.0-SNAPSHOT</version>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.2.3</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.6.1</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

    </dependencies>

</project>
2.1、关于shiro加密的说明

Shiro提供了base64和16进制字符串编码/解码的API支持,方便一些编码解码操作。Shiro内部的一些数据的存储/表示都使用了base64和16进制字符串,实现不同的加密算法如下:

产生随机的“盐值”算法

package com.sudojava.shiro.commons;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.crypto.RandomNumberGenerator;
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ByteSource;

public class RandomNumberUtils {
    //随机数的代码生成器
    private static RandomNumberGenerator generator;

    static {
        generator = new SecureRandomNumberGenerator();
    }

    /**
     * 获取工具类
     * @return
     */
    public static ByteSource getByteSource(){
        if (generator!=null){
            return  generator.nextBytes();
        }
        return  null;
    }
}

各种不同的加密算法如下:

package com.sudojava.shiro.cryptography;

import com.sudojava.shiro.commons.RandomNumberUtils;
import org.apache.shiro.crypto.hash.*;

public class CryptoUtils {


    protected static Object getSalt() {
        return RandomNumberUtils.getByteSource().toHex();
    }

    public static String cryptoObject(String source,String algorithm,int hashIterations) {
        switch (algorithm) {
            case "md5": {

                Md5Hash md5Hash = new Md5Hash(source,getSalt(),hashIterations);
                return  md5Hash.toHex();
            }
            case "md2":
            {
                Md2Hash md2Hash = new Md2Hash(source,getSalt(),hashIterations);
                return md2Hash.toHex();
            }
            case "sha1":
            {
                Sha1Hash sha1Hash = new Sha1Hash(source,getSalt(),hashIterations);
                return  sha1Hash.toHex();
            }
            case "sha256":
            {
                Sha256Hash sha256Hash = new Sha256Hash(source,getSalt(),hashIterations);
                return  sha256Hash.toHex();
            }
            default:
                return null;
        }

    }


}

测试加密算法结果:

package com.sudojava.shiro.test;

import com.sudojava.shiro.cryptography.CryptoUtils;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.junit.Test;

public class TestSimpleHash {

    @Test
    public void testMethod() {
        String username = "admin";
        String result1 = CryptoUtils.cryptoObject(username, "md5", 4);
        System.out.println(result1);

        String result2 = CryptoUtils.cryptoObject(username, "md2", 4);
        System.out.println(result2);
        String result3 = CryptoUtils.cryptoObject(username, "sha1", 4);
        System.out.println(result3);

    }
}
第三章:Shiro身份认证

用户身份验证,在应用程序中做身份以及角色的认证。一般都是使用用户名和密码作为认证的凭据。

在shiro中,用户需要提供principals (身份)和credentials(证明)给shiro,从而应用能验证用户身份:

principals:身份,即主体的标识属性,可以是任何东西,如用户名、邮箱等,唯一即可。一个主体可以有多个principals,但只有一个Primary principals,一般是用户名/密码/手机号。

credentials:证明/凭证,即只有主体知道的安全值,如密码/数字证书等。

最常见的principals和credentials组合就是用户名/密码了。

第一步:

配置shiro.ini文件,采用硬编码的方式提供认证信息

# =============================================================================
# Tutorial INI configuration
#
# Usernames/passwords are based on the classic Mel Brooks' film "Spaceballs" :)
# =============================================================================

# -----------------------------------------------------------------------------
# Users and their (optional) assigned roles
# username = password, role1, role2, ..., roleN
# -----------------------------------------------------------------------------
[users]
root = secret, admin
guest = guest, guest
presidentskroob = 12345, president
darkhelmet = ludicrousspeed, darklord, schwartz
lonestarr = vespa, goodguy, schwartz

配置规则:

用户名 = 密码,角色1,角色2,角色3等

模拟用户登录情景

用户登录类:Authorization

package com.sudojava.shiro.authorization;

import com.sudojava.shiro.domain.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class Authorization {

    private static final transient Logger log = LoggerFactory.getLogger(Authorization.class);

    private Factory<SecurityManager> factory;

    private SecurityManager manager;

    public Authorization(String shiro_ini) {
        //获取SecurityManager工厂,加载shiro.ini文件
        factory = new IniSecurityManagerFactory(shiro_ini);
        //得到SecurityManager实例,并绑定给SecurityUtils
        manager = factory.getInstance();
    }

    /**
     * 模拟用户登录过程
     *
     * @param user
     * @return
     */
    public boolean login(User user) {
        boolean flag = false;
        SecurityUtils.setSecurityManager(manager);
        //得到Subject及创建用户名/密码身份验证Token(即用户身份/凭证)
        Subject currentUser = SecurityUtils.getSubject();
        if (!currentUser.isAuthenticated()) {
            UsernamePasswordToken token =
                    new UsernamePasswordToken(user.getUsername(), user.getPassword());
            token.setRememberMe(true);//记住用户登录信息

            try {
                currentUser.login(token);//执行登录操作,进行身份验证
                flag = true;
            } catch (UnknownAccountException e) {
                log.info("未知用户账号异常");
            } catch (IncorrectCredentialsException e) {
                log.info("凭证不正确");
            } catch (ExcessiveAttemptsException e) {
                log.info("尝试认证次数多于系统指定次数");
            } catch (AuthenticationException e) {
                //所有认证异常的父类
                log.info("其他未知错误!");
            }

        }
        currentUser.logout();//用户登出操作
        return flag;
    }


}

测试用户登录结果:

package com.sudojava.shiro.authorization;

import com.sudojava.shiro.domain.User;
import org.junit.Before;
import org.junit.Test;

public class TestAuthorization {
    private Authorization authorization;
    @Before
    public void init(){
        authorization = new Authorization("classpath:shiro.ini");
    }
    @Test
    public void login(){
        User user = new User();
        user.setPassword("guest");
        user.setUsername("guest");
       boolean flag =  authorization.login(user);
       if (flag){
           System.out.println("登录成功");
       }else{
           System.out.println("登录失败");
       }
    }
}
第四章:Shiro 授权机制

在shiro中规定了授权的基本对象包括:Subject、Role和Permission和访问资源等四个主体对象。

Subject:主体

主体,指的是访问应用的用户,一般通知User对象。

Resource:资源

通常是指工程下资源模块,也可以指定特定的业务。

Role:角色

角色代表主体的身份特征,一般一种角色下可以拥有多种主体,所以是一对多的关系,例如:超级管理员、经理、CTO等。

Permission:权限

即指定授予主体操作某一些资源的权力,比如,查看、删除和修改的权力等。

授权方式

Shiro授权方式分为三种:

1、Java代码实现

通过Subject对象实现

2、注解实现

通过@RequireXXX实现

3、在web页面上实现

通过实现

案例讲解

模拟用户登录之后,判断该用户是否拥有一定权限,如果该用户有指定的权限,那么可以执行删除或者修改功能

配置shiro.ini文件

# =============================================================================
# Tutorial INI configuration
#
# Usernames/passwords are based on the classic Mel Brooks' film "Spaceballs" :)
# =============================================================================

# -----------------------------------------------------------------------------
# Users and their (optional) assigned roles
# username = password, role1, role2, ..., roleN
# -----------------------------------------------------------------------------
[users]
root = secret, admin
guest = guest, guest
presidentskroob = 12345, president
darkhelmet = ludicrousspeed, darklord, schwartz
lonestarr = vespa, goodguy, schwartz
jack = 123456,admin

# -----------------------------------------------------------------------------
# Roles with assigned permissions
# roleName = perm1, perm2, ..., permN
# -----------------------------------------------------------------------------
[roles]

admin = user:create,user:update,user:delete
schwartz = lightsaber:*
goodguy = winnebago:drive:eagle5

核心BasicRole类:

我们声明一个抽象类,指定登录方法为抽象的方法

package com.sudojava.shiro.role;

import com.sudojava.shiro.authorization.Authorization;
import com.sudojava.shiro.domain.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public abstract class BasicRole<T> {



    private Factory<SecurityManager> factory;

    private SecurityManager manager;

    public BasicRole(String shiro_ini) {
        //获取SecurityManager工厂,加载shiro.ini文件
        factory = new IniSecurityManagerFactory(shiro_ini);
        //得到SecurityManager实例,并绑定给SecurityUtils
        manager = factory.getInstance();
    }

    public Factory<SecurityManager> getFactory() {
        return factory;
    }

    public void setFactory(Factory<SecurityManager> factory) {
        this.factory = factory;
    }

    public SecurityManager getManager() {
        return manager;
    }

    public void setManager(SecurityManager manager) {
        this.manager = manager;
    }

    public abstract boolean login(T t);


}

核心登录LoginRole类

package com.sudojava.shiro.role;

import com.sudojava.shiro.authorization.Authorization;
import com.sudojava.shiro.domain.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LoginRole extends BasicRole<User> {

    private static final transient Logger log = LoggerFactory.getLogger(Authorization.class);


    public LoginRole(String shiro_ini) {
        super(shiro_ini);
    }

    @Override
    public boolean login(User user) {
        SecurityUtils.setSecurityManager(getManager());
        Subject subject = SecurityUtils.getSubject();
        if (!subject.isAuthenticated()) {
            UsernamePasswordToken token =
                    new UsernamePasswordToken(user.getUsername(), user.getPassword());
            token.setRememberMe(true);
            try {
                subject.login(token);
                if (subject.hasRole("admin")) {
                    log.info("该用户拥有admin角色身份");
                    if (subject.isPermitted("user:delete")) {
                        log.info("该用户具有删除选项功能");
                        subject.execute(new Runnable() {
                            @Override
                            public void run() {
                                try {
                                    Thread.sleep(3000);
                                    log.info("执行删除操作");
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }

                            }
                        });
                    }
                }
                return true;
            } catch (UnknownAccountException e) {
                log.info("未知用户");
            }

        }

        return false;
    }
}

核心测试类

package com.sudojava.shiro.role;

import com.sudojava.shiro.domain.User;
import org.junit.Before;
import org.junit.Test;

public class TestRole {

    private LoginRole loginRole;

    @Before
    public void  init(){
        loginRole = new LoginRole("classpath:shiro.ini");
    }

    @Test
    public void test(){
        User user = new User();
        user.setUsername("root");
        user.setPassword("secret");
        boolean flag = loginRole.login(user);
        System.out.println(flag);
    }
}

关于Shiro.ini配置文件的说明

~~~ini

—————————————————————————–

Users and their (optional) assigned roles

username = password, role1, role2, …, roleN

—————————————————————————–

[users]
root = secret, admin
guest = guest, guest
presidentskroob = 12345, president
darkhelmet = ludicrousspeed, darklord, schwartz
lonestarr = vespa, goodguy, schwartz
jack = 123456,admin
~~~

配置shiro.ini用户如下规则:

[users]是标志用户信息,规则如下:

用户名 = 密码,角色1,角色2,角色3……

分析数据结构:一个用户可以拥有多个角色

~~~ini

—————————————————————————–

Roles with assigned permissions

roleName = perm1, perm2, …, permN

—————————————————————————–

[roles]

admin = user:create,user:update,user:delete
schwartz = lightsaber:*
goodguy = winnebago:drive:eagle5

~~~

[roles] 是标志用户角色拥有什么权限,规则如下:

资源:用户操作

例如:

admin = user:view

admin 拥有user的查看权限

admin = user:create,user:update,user:delete

admin 拥有user的创建和修改、删除权限

admin =user:*

admin 拥有user下的所有权限

admin = system:user :create, delete,view,find

admin 拥有system:user 下的CRUD功能

第五章:Shiro Realm认证流程

在Shiro中Realm(域)可以访问特定的应用程序安全实体(如:用户、角色和权限)以确定身份验证和授权操作。

Realm通常和数据源有关系,例如关系型数据库、文件系统等,具有一对一的对应关系。Realm接口实现使用特定

数据源的API接口访问数据,也就是我们可以在数据库中直接定义 用户表角色表和权限表等,我们可以使用JDBC、Hibernate、JPA的规范访问,Realm本质上是数据访问层的DAO。

由于每个应用程序不同,用户和角色等安全数据可以以多种方式进行表示。 Shiro尝试尽可能保持非侵入性的开发哲学 - 它不要求您实现或扩展任何用户,组或角色接口或类

大多数用户不会直接实现Realm接口,而是扩展其中一个子类AuthenticatingRealm或AuthorizingRealm,大大降低了从头开始实现Realm的工作量。

AuthenticatingRealm类的介绍

该类主要是实现了验证模块的信息,通过它可以实现一系列用户登录校验和角色判断等功能,通常该类是配合CacheManager一起使用的,后面我们在详细介绍。

代码示例

第一步配置shiro_realm.ini文件

[main]
#声明一个realm
myRealm1 = com.sudojava.shiro.realm.LoginRealm
#指定securityManager的realms实现
securityManager.realms=$myRealm1

第二步继承AuthenticatingRealm类

package com.sudojava.shiro.realm;

import org.apache.shiro.authc.*;
import org.apache.shiro.realm.AuthenticatingRealm;

/**
 * 可以使用Realm获取数据库的基本信息,
 * 包括User表、权限表、和角色表
 */
public class LoginRealm extends AuthenticatingRealm {
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //取出用户名和密码
        String username = token.getPrincipal().toString();
        String password = new String((char[]) token.getCredentials());
        if(!"zhang".equals(username)) {
            throw new UnknownAccountException(); //如果用户名错误
        }
        if(!"123".equals(password)) {
            throw new IncorrectCredentialsException(); //如果密码错误
        }
        return new SimpleAuthenticationInfo(username,password,getName());
    }
}

备注:采用硬编码的方式,模拟数据库的账号为:zhang 密码为:123

第三步Login类的实现

package com.sudojava.shiro.realm;

import com.sudojava.shiro.authorization.Authorization;
import com.sudojava.shiro.domain.User;
import com.sudojava.shiro.role.BasicRole;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Login extends BasicRole<User> {

    private static final  Logger log = LoggerFactory.getLogger(Authorization.class);


    public Login(String shiro_ini) {
        super(shiro_ini);
    }

    @Override
    public boolean login(User user) {
        SecurityUtils.setSecurityManager(getManager());
        Subject subject = SecurityUtils.getSubject();
        if (!subject.isAuthenticated()){
            UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(),user.getPassword());
            token.setRememberMe(true);
            try {
               subject.login(token);
               return  true;
            }catch (UnknownAccountException e){
                log.info("账号不匹配");
            }
        }
        return false;
    }
}

第四步测试类的实现

package com.sudojava.shiro.realm;

import com.sudojava.shiro.domain.User;
import org.junit.Before;
import org.junit.Test;

public class TestRealm {
    private Login login;
    @Before
    public void init(){
        login = new Login("classpath:shiro_realm.ini");
    }

    @Test
    public void login(){
        User user = new User();
        user.setUsername("zhang");
        user.setPassword("123");
        boolean flag = login.login(user);
        System.out.println(flag);
    }

}
第六章:Shiro 使用JdbcRealm进行验证

我们先来看看Shiro的认证流程

wKiom1ePJu3CpsHXAAJXWZSp3ME963

从上面的继承关系图,我们可以看出来一般都是继承AuthorizingRealm就可以了,自带缓存的功能。

Shiro Realm主要默认实现:

  • org.apache.shiro.realm.text.IniRealm:
    ​ 通过ini文件配置进行验证,

    ​ [users]部分指定用户名/密码及其角色;

    ​ [roles]部分指定角色即权限信息;

  • org.apache.shiro.realm.text.PropertiesRealm:
    ​ user.username=password,role1,role2 标志用户名密码和角色
    ​ role.role1=permission1,permission2 标志角色和权限信息

  • org.apache.shiro.realm.jdbc.JdbcRealm:

    我们可以通过创建对应的表,使用sql语句查询出来对应的信息:

    获取用户密码:“select“select password from users where username = ?”,
    获取用户密码及盐:“select password, password_salt from users where username = ?”
    获取用户角色:“select role_name from user_roles where username = ?”
    获取角色对应的权限信息:“select permission from roles_permissions where role_name = ?”
    也可以调用相应的api进行自定义sql。

使用JdbcRealm进行验证示例步骤:

第一步:创建shiro-jdbc-realm.ini文件

[main]
# 配置JDBC数据库连接
dataSource=com.alibaba.druid.pool.DruidDataSource
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql://localhost:3306/shiro
dataSource.username=root
dataSource.password=root

# JdbcRealm
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
# 允许授权查询
jdbcRealm.permissionsLookupEnabled = true
jdbcRealm.dataSource=$dataSource

#编写查询数据库的SQL语句
jdbcRealm.authenticationQuery = select password from users where username = ?
jdbcRealm.userRolesQuery = select a.role_name from user_roles a,users b where a.users_id = b.id  and  b.username = ?
jdbcRealm.permissionsQuery =select a.permission FROM roles_permissions a,user_roles b  where b.role_id = a.user_roles_role_id and b.role_name = ?
securityManager.realms=$jdbcRealm

第二步:在pom.xml文件中添加DataSource,使用的是阿里巴巴的数据驱动

<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.0</version>
</dependency>
<!-- 添加mysql数据驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.40</version>
</dependency>

第三步:创建用户表users、user_roles、roles_permissions表

~~~mysql
CREATE TABLE IF NOT EXISTS shiro.users (
id INT NOT NULL,
username VARCHAR(100) NULL,
password VARCHAR(100) NULL,
password_salt VARCHAR(100) NULL,
PRIMARY KEY (id))
ENGINE = InnoDB

CREATE TABLE IF NOT EXISTS shiro.user_roles (
role_id INT NOT NULL,
role_name VARCHAR(100) NULL,
users_id INT NOT NULL,
PRIMARY KEY (role_id),
INDEX fk_user_roles_users1_idx (users_id ASC),
CONSTRAINT fk_user_roles_users1
FOREIGN KEY (users_id)
REFERENCES shiro.users (id)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB

CREATE TABLE IF NOT EXISTS shiro.roles_permissions (
permission VARCHAR(100) NULL,
p_id INT NOT NULL,
user_roles_role_id INT NOT NULL,
PRIMARY KEY (p_id),
INDEX fk_roles_permissions_user_roles1_idx (user_roles_role_id ASC),
CONSTRAINT fk_roles_permissions_user_roles1
FOREIGN KEY (user_roles_role_id)
REFERENCES shiro.user_roles (role_id)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB
~~~

备注:请校验数据库的名称。

第四步:创建JDBCRealm相关的类

package com.sudojava.shiro.jdbcrealm;

import com.sudojava.shiro.authorization.Authorization;
import com.sudojava.shiro.domain.User;
import com.sudojava.shiro.basic.BasicShiro;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.Permission;
import org.apache.shiro.authz.permission.WildcardPermission;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LoginForJdbcRealm  extends BasicShiro<User> {

    private static final Logger log = LoggerFactory.getLogger(Authorization.class);

    public LoginForJdbcRealm(String shiro_ini) {
        super(shiro_ini);
    }

    @Override
    public boolean login(User user) {
        SecurityUtils.setSecurityManager(getManager());
        Subject subject = SecurityUtils.getSubject();
        if (!subject.isAuthenticated()){
            UsernamePasswordToken token  =
                    new UsernamePasswordToken(user.getUsername(),user.getPassword());
            try {
                subject.login(token);
                token.setRememberMe(true);
                if (subject.hasRole("manager")){
                    log.info("该用户拥有 manager 角色");
                    subject.checkPermission("delete");

                    if (subject.isPermitted("delete")){
                        log.info("该用户具有删除的权限");
                        subject.execute(new Runnable() {
                            @Override
                            public void run() {
                                try {

                                    Thread.sleep(3000);
                                    log.info("删除资源中......");
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }

                            }
                        });
                    }
                }
                return true;
            } catch (UnknownAccountException e) {
               log.info(e.getMessage()+"账号名有误");
            }
        }
        return false;
    }
}

模拟用户登录操作

package com.sudojava.shiro.jdbc;

import com.sudojava.shiro.domain.User;
import com.sudojava.shiro.jdbcrealm.LoginForJdbcRealm;
import org.junit.Before;
import org.junit.Test;

public class TestForJdbc {

    private LoginForJdbcRealm realm;

    @Before
    public void setup(){
        realm = new LoginForJdbcRealm("classpath:shiro-jdbc-realm.ini");
    }

    @Test
    public void login(){
        User user = new User();
        user.setPassword("123");
        user.setUsername("admin");
        boolean flag = realm.login(user);
        System.out.println(flag);
    }
}
第七章:使用原生的Java代码对Realm类进行校验和授权操作

在shiro中我们可以采用Java代码对JdbcRealm进行封装,直接在登录模块进行校验操作。

手动封装Java类

package com.sudojava.shiro.nativeRealm;

import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.jdbc.JdbcRealm;



public class RealmDataSource {

    private MysqlDataSource dataSource;
    private DefaultSecurityManager securityManager;
    private JdbcRealm jdbcRealm;

    public RealmDataSource() {
        try {

            dataSource = new MysqlDataSource();
            dataSource.setUser("root");
            dataSource.setPassword("root");
            dataSource.setServerName("localhost");
            dataSource.setLoginTimeout(2);
            dataSource.setUrl("jdbc:mysql://localhost:3306/shiro");
            jdbcRealm = new JdbcRealm();
            jdbcRealm.setDataSource(dataSource);
            jdbcRealm.setPermissionsLookupEnabled(true);
            //验证
            String authentication_sql = "select password from users where username = ?";
            jdbcRealm.setAuthenticationQuery(authentication_sql);
            //验证角色
            String user_roles_sql = "select a.role_name from user_roles a,users b where a.users_id = b.id  and  b.username = ?";
            jdbcRealm.setUserRolesQuery(user_roles_sql);
            String permission_sql = "select a.permission FROM roles_permissions a,user_roles b  where b.role_id = a.user_roles_role_id and b.role_name = ?";
            jdbcRealm.setPermissionsQuery(permission_sql);
            securityManager = new DefaultSecurityManager(jdbcRealm);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public DefaultSecurityManager getSecurityManager() {
        return securityManager;
    }

    /**
     * @return
     */
    public MysqlDataSource getDataSource() {
        if (dataSource != null) {
            return dataSource;
        }
        return null;
    }


}

模拟用户登录操作

package com.sudojava.shiro.nativeRealm;

import com.sudojava.shiro.authorization.Authorization;
import com.sudojava.shiro.domain.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class NativeLogin {

    private static final Logger log = LoggerFactory.getLogger(Authorization.class);


    private RealmDataSource dataSource;

    public NativeLogin() {
        dataSource = new RealmDataSource();
    }

    public boolean login(User user) {
        boolean flag = false;
        SecurityUtils.setSecurityManager(dataSource.getSecurityManager());
        Subject subject = SecurityUtils.getSubject();
        if (!subject.isAuthenticated()) {
            UsernamePasswordToken token =
                    new UsernamePasswordToken(user.getUsername(), user.getPassword());
            token.setRememberMe(true);
            try {
                subject.login(token);
                System.out.println("login successfully");
                flag = true;
                if (subject.hasRole("manager")){
                    log.info("该用户拥有 manager 角色");
                    subject.checkPermission("delete");

                    if (subject.isPermitted("delete")){
                        log.info("该用户具有删除的权限");
                        subject.execute(new Runnable() {
                            @Override
                            public void run() {
                                try {

                                    Thread.sleep(3000);
                                    log.info("删除资源中......");
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }

                            }
                        });
                    }
                }
            } catch (UnknownAccountException e) {
                e.printStackTrace();
            }
        }
        return flag;
    }
}

测试Java类

package com.sudojava.shiro.nativerealm;

import com.sudojava.shiro.domain.User;
import com.sudojava.shiro.nativeRealm.NativeLogin;
import org.junit.Before;
import org.junit.Test;

public class TestNative {

    private NativeLogin nativeLogin;

    @Before
    public void setup(){
        nativeLogin = new NativeLogin();
    }

    @Test
    public void loginUser(){
        User user = new User();
        user.setPassword("123");
        user.setUsername("admin");
        boolean flag = nativeLogin.login(user);
        System.out.println(flag);
    }
}
第八章:Shiro Web整合以及实现等验证操作

Shiro提供了与Web集成环境的安全应用支持,

我们在Web工程下的web.xml配置简单的ShiroFilter来控制所有的URL请求,并且根据请求进行转发,

其通过一个ShiroFilter入口来拦截需要安全控制的URL,然后进行相应的控制,ShiroFilter类似于如Strut2/SpringMVC这种web框架的前端控制器,其是安全控制的入口点,其负责读取配置(如ini配置文件),然后判断URL是否需要登录/权限等工作。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值