Shiro框架02(自定义realm、算法加密、授权)

Shiro框架02

1.自定义realm

[1]为什么使用自定义realm

我们使用JDBCRealm的时候发现,shiro的底层自己封装了数据库表的名称和字段的名称,这样就造成了使用起来非常不方便。

[2]自定义realm代码实现

自定义realm可以实现Realm接口,也可以实现Realm接口下的子方法,根据实际情况进行选择。这里以继承AuthorizingRealm 方法为例。
A、自定义realm

package cn.qt.shiro2;

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.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

import java.sql.*;

public class UserRealm extends AuthorizingRealm {

    //认证
    /*
    * 参数   AuthenticationToken authenticationToken
    * authenticationToken.getPrincipal()为登录的用户名,是
    * UsernamePasswordToken token = new UsernamePasswordToken("root1","1111");中的参数1,即客户端登录的用户名
    * */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

        Connection  conn=null;
        PreparedStatement ps=null;
        ResultSet rs=null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/shiro", "root", "1111");
           //查询数据库中的数据,把数据给SimpleAuthenticationInfo
            ps = conn.prepareStatement("select pwd from admin where uname=?");
            ps.setObject(1,authenticationToken.getPrincipal());
            rs = ps.executeQuery();
            while (rs.next()){
                //把数据给SimpleAuthenticationInfo,用于认证
            SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(authenticationToken.getPrincipal(),rs.getString("pwd"),"userRealm") ;
                return info;
            }

        }catch (Exception e){
           e.printStackTrace();
        }finally {
            try {
                rs.close();
                ps.close();
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

}

B、ini文件

[main]
userRealm= cn.qt.shiro2.UserRealm
#把userRealm赋值给securityManager
securityManager.realms=$userRealm

C、测试文件

package cn.qt.shiro2;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
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;

public class TestB {
    public static void main(String[] args) {

        //[1]解析Shiro.ini文件
        Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:shiro-jdbc2.ini");
        //[2]通过SecurityManager工厂获得SecurityManager实例
        SecurityManager securityManager = factory.getInstance();
        //[3]把SecurityManager对象设置到运行环境中
        SecurityUtils.setSecurityManager(securityManager);
        //[4]通过SecurityUtils获得Subject
        Subject subject = SecurityUtils.getSubject();
        //[5]书写自己的账户和密码----相当于用户自己输入的账户和密码
        //我们拿自己书写的账户和密码去和shiro.ini中的账户密码比较
        UsernamePasswordToken token = new UsernamePasswordToken("root","1111");
        //[6]进行身份的验证
        try {
            subject.login(token);
            //[7]通过方法判断是否登录成功
            if (subject.isAuthenticated()){
                System.out.println("登录成功");
            }
        } catch (UnknownAccountException e) {//用户名错误
            System.out.println("用户名错误");
        }catch (IncorrectCredentialsException e){//密码错误
            System.out.println("密码错误");
        }
    }
}

2.散列算法(算法加密)

[1]加密的介绍

在身份认证的过程中往往都会涉及到加密,如果不加密,信息就会非常不安全。Shiro中提供的加密算法比较多,如md5,sha等。
使用MD5进行加密
加密本身的设计是一个不可逆的过程,但是有些解密网站可以解开一些由简单的数据加密后的密文,如数字和字母的组合。因此密码的安全性取决于密码本身的复杂程度和加密次数。

		//用md5的方式加密1111
        Md5Hash md5=new Md5Hash("1111");

(1)把1111进行加密:得到结果为b59c67bf196a4758191e42f76670ceba
(2)把1111进行加盐(qt)加密:得到结果为f769a4dfcb50d34b2de500a4eb8eea74

		 //md5加盐,即把加密数据1111和盐qt,在一起加密
       	Md5Hash  md5=new Md5Hash("1111","qt");

盐一般经过设计后存储在数据库中。
如果盐中也是数字和字母的组合,仍会被解密网站成功破译。
加盐后破译结果为qt1111,即加密方式为:(盐+数据)加密。
(3)把(2)中的数据进行迭代加密,迭代次数为2:得到结果为9880b3c88f213fe936b0de904ccb598f

		//迭代
       Md5Hash   md5=new Md5Hash("1111","qt",2);

加密中的迭代指的是加密次数,迭代次数为2,就是把数据加密2次。
进行过迭代2次以上加密不容易被破解,迭代次数越多数据越安全,但是效率就越低。
影响数据安全的因素:
a.数据本身的复杂程度数字+字母+特殊字符。其中最主要的是特殊字符。
b.加密方式的保密性,即很少人知道用的是何种加密方式。
c.加盐
d.迭代次数

[2]使用shiro进行身份认证

Shiro中提供了多种加密方式
在这里插入图片描述
用户登录时进行加密操作(用户登录时输入的是未加密的密码)。
A、配置ini文件
在这里插入图片描述
B、自定义Realm文件中进行加盐操作
在这里插入图片描述

[3]代码实现

数据库中存放的是加密后的数据
A、ini文件

[main]
#把userRealm赋值给securityManager
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
credentialsMatcher.hashAlgorithmName=md5
credentialsMatcher.hashIterations=2

userRealm= cn.qt.shiro3.UserRealm
userRealm.credentialsMatcher=$credentialsMatcher
securityManager.realms=$userRealm

B、自定义Realm文件

package cn.qt.shiro3;

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.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

import java.sql.*;

public class UserRealm extends AuthorizingRealm {

    //认证
    /*
    * 参数   AuthenticationToken authenticationToken
    * authenticationToken.getPrincipal()为登录的用户名,是
    * UsernamePasswordToken token = new UsernamePasswordToken("root1","1111");中的参数1,即客户端登录的用户名
    * */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

        Connection  conn=null;
        PreparedStatement ps=null;
        ResultSet rs=null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/shiro", "root", "1111");
           //查询数据库中的数据,把数据给SimpleAuthenticationInfo
            ps = conn.prepareStatement("select pwd from admin where uname=?");
            ps.setObject(1,authenticationToken.getPrincipal());
            rs = ps.executeQuery();
            while (rs.next()){
                //把数据给SimpleAuthenticationInfo,用于认证
                SimpleAuthenticationInfo info=new SimpleAuthenticationInfo
              (authenticationToken.getPrincipal(),rs.getString("pwd"), ByteSource.Util.bytes("qt"),"userRealm");
                return info;
            }

        }catch (Exception e){
           e.printStackTrace();
        }finally {
            try {
                rs.close();
                ps.close();
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }
}

C、Test文件

package cn.qt.shiro3;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
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;

public class TestA {
    public static void main(String[] args) {

        //[1]解析Shiro.ini文件
        Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:shiro-jdbc3.ini");
        //[2]通过SecurityManager工厂获得SecurityManager实例
        SecurityManager securityManager = factory.getInstance();
        //[3]把SecurityManager对象设置到运行环境中
        SecurityUtils.setSecurityManager(securityManager);
        //[4]通过SecurityUtils获得Subject
        Subject subject = SecurityUtils.getSubject();
        //[5]书写自己的账户和密码----相当于用户自己输入的账户和密码
        //我们拿自己书写的账户和密码去和shiro.ini中的账户密码比较
        UsernamePasswordToken token = new UsernamePasswordToken("root","1111");
        //[6]进行身份的验证
        try {
            subject.login(token);
            //[7]通过方法判断是否登录成功
            if (subject.isAuthenticated()){
                System.out.println("登录成功");
            }
        } catch (UnknownAccountException e) {//用户名错误
            System.out.println("用户名错误");
        }catch (IncorrectCredentialsException e){//密码错误
            System.out.println("密码错误");
        }
    }
}

3.授权

[1]授权中名词的解释

授权:指给身份认证通过的人授予某些资源的访问权限。
权限的粒度:粗粒度、细粒度
粗粒度:如User具有CRUD的操作 通常指的是表的操作
细粒度:如只允许查询id=1的用户 通常使用业务代码实现
shiro的授权是细粒度

[2]shiro中授权的3种方式

A、编程式
ini文件

#指定具体的用户
[users]
zs=123,role1,role2
root=1111,role2
#角色的定义
[roles]
role1=add,delete,update
role2=find

判断方法

//[1]解析Shiro.ini文件
        Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:shiro.ini");
        //[2]通过SecurityManager工厂获得SecurityManager实例
        SecurityManager securityManager = factory.getInstance();
        //[3]把SecurityManager对象设置到运行环境中
        SecurityUtils.setSecurityManager(securityManager);
        //[4]通过SecurityUtils获得Subject
        Subject subject = SecurityUtils.getSubject();
        //[5]书写自己的账户和密码----相当于用户自己输入的账户和密码
        //我们拿自己书写的账户和密码去和shiro.ini中的账户密码比较
        UsernamePasswordToken token = new UsernamePasswordToken("zs","123"); 
	 //[6]进行身份的验证
        try {
            subject.login(token);
        } catch (UnknownAccountException e) {//用户名错误
            System.out.println("用户名错误");
        }catch (IncorrectCredentialsException e){//密码错误
            System.out.println("密码错误");
        }


//授权的查询方法
        //【一】基于角色的授权,判断user中的角色
        //A、判断用户是否具有指定角色
        boolean flag = subject.hasRole("role1");
        boolean[] booleans = subject.hasRoles(Arrays.asList("role1", "role2"));
        for (boolean b:booleans) {
            System.out.println(b);
        }
        //B、检查User是否具有该角色
        //该方法为void类型,若不具备该权限则抛出异常UnauthorizedException
        subject.checkRole("role1");
        subject.checkRoles("role1","role2");

        //【二】基于资源授权,判断该角色中是否含有该资源
        //A、检查角色是否有指定资源
        boolean flag2 = subject.isPermitted("find");
        boolean permittedAll = subject.isPermittedAll("find", "update", "delete");
        System.out.println(permittedAll);
        //B、判断角色是否具有指定资源
        //该方法类型为void,若角色没有指定资源,则抛出异常UnauthorizedException
        subject.checkPermission("sa");
        subject.checkPermissions("find","update","add","delete");

B、注解式
一般应用于Web程序

@RequiresRoles("管理员")
    public void abc(){

    }

C、标签配置
在jsp页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<html>
  <head>
    <title>$Title$</title>
  </head>
  <body>
  <shiro:hasPermission name="update">
    <a>更新操作</a>
  </shiro:hasPermission>
  </body>
</html>
[3]自定义Realm实现授权

我们仅仅通过配置文件实现授权是非常不灵活的,在时机用户中我们将实际的用户信息和权限保存到数据库中,我们是从数据库中获得的用户信息,使用JDBCRealm进行用户授权。使用JDBCRealm进行的操作也不灵活,所以我们自定义realm进行授权。
我们在1.自定义realm的基础上进行授权操作
自定义的UserRealm方法
在这里插入图片描述
在登录时认证
在这里插入图片描述
自定义Realm代码

package cn.qt.shiro;

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.subject.PrincipalCollection;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class UserRealm extends AuthorizingRealm {

    //认证
    /*
    * 参数   AuthenticationToken authenticationToken
    * authenticationToken.getPrincipal()为登录的用户名,是
    * UsernamePasswordToken token = new UsernamePasswordToken("root1","1111");中的参数1,即客户端登录的用户名
    * */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

        Connection  conn=null;
        PreparedStatement ps=null;
        ResultSet rs=null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/shiro", "root", "1111");
           //查询数据库中的数据,把数据给SimpleAuthenticationInfo
            ps = conn.prepareStatement("select pwd from admin where uname=?");
            ps.setObject(1,authenticationToken.getPrincipal());
            rs = ps.executeQuery();
            while (rs.next()){
                //把数据给SimpleAuthenticationInfo,用于认证
                SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(authenticationToken.getPrincipal(),rs.getString("pwd"), "userRealm") ;
                return info;
            }

        }catch (Exception e){
           e.printStackTrace();
        }finally {
            try {
                rs.close();
                ps.close();
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //获得用户登录时传入的用户名
        String username = principalCollection.getPrimaryPrincipal().toString();
        //获得username,然后去数据库查询这个角色对应的角色,根据角色查询角色对应的菜单
        //返回给指定角色下的所有菜单
        System.out.println(username);

        //模拟数据库查询到的菜单
        List<String> list =new ArrayList<>();
        list.add("findUser");
        list.add("updateUser");
        list.add("deleteUser");

        SimpleAuthorizationInfo simpleAuthorizationInfo=new SimpleAuthorizationInfo();
        for (String l:list) {
            simpleAuthorizationInfo.addStringPermission(l);
        }
        return simpleAuthorizationInfo;
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值