springboot2.x(五)安全框架2-shiro安基础知识

一、shiro基础概念

1.什么是shiro

Shiro 是 Java 的一个安全框架。目前,使用 Apache Shiro 的人越来越多,因为它相 当简单,对比 Spring

Security,可能没有 Spring Security 做的功能强大,但是在实际工作时 可能并不需要那么复杂的东西,所以使用小而简单的

Shiro 就足够

shiri框架图

Authentication:身份认证/登录,验证用户是不是拥有相应的身份;

Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用

户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用

户对某个资源是否具有某个权限;

Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信

息都在会话中;会话可以是普通 JavaSE 环境的,也可以是如 Web 环境的;

Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;

Web Support:Web 支持,可以非常容易的集成到 Web 环境;

Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;

Concurrency:shiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能

把权限自动传播过去;

Testing:提供测试支持;

Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;

Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录

记住一点,Shiro 不会去维护用户、维护权限;这些需要我们自己去设计/提供;然后通过

相应的接口注入给 Shiro 即可。

2.shiro实现原理理解

 

Subject:主体,可以看到主体可以是任何可以与应用交互的“用户”;

SecurityManager : 相 当 于 SpringMVC 中 的 DispatcherServlet 或 者 Struts2 中的

FilterDispatcher;是 Shiro 的心脏;所有具体的交互都通过 SecurityManager 进行控制;它管理着所有 Subject、且负责进行认证和授权、及会话、缓存的管理。

Authenticator:认证器,负责主体认证的,这是一个扩展点,如果用户觉得 Shiro 默认的不好,可以自定义实现;其需要认证策略(Authentication Strategy),即什么情况下算用户认证通过了;

Authrizer:授权器,或者访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能;

Realm:可以有 1 个或多个 Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是 JDBC 实现,也可以是 LDAP 实现,或者内存实现等等;由用户提供;注意:Shiro不知道你的用户/权限存储在哪及以何种格式存储;所以我们一般在应用中都需要实现自己的 Realm;

SessionManager:如果写过 Servlet 就应该知道 Session 的概念,Session 呢需要有人去管理它的生命周期,这个组件就是 SessionManager;而 Shiro 并不仅仅可以用在 Web 环境,也可以用在如普通的 JavaSE 环境、EJB 等环境;所有呢,Shiro 就抽象了一个自己的 Session来管理主体与应用之间交互的数据;这样的话,比如我们在 Web 环境用,刚开始是一台Web 服务器;接着又上了台 EJB 服务器;这时想把两台服务器的会话数据放到一个地方,

这个时候就可以实现自己的分布式会话(如把数据放到 Memcached 服务器);

SessionDAO:DAO 大家都用过,数据访问对象,用于会话的 CRUD,比如我们想把 Session保存到数据库,那么可以实现自己的 SessionDAO,通过如 JDBC 写到数据库;比如想把Session 放到 Memcached 中,可以实现自己的 Memcached SessionDAO;另外 SessionDAO中可以使用 Cache 进行缓存,以提高性能;

CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本

上很少去改变,放到缓存中后可以提高访问的性能

Cryptography:密码模块,Shiro 提高了一些常见的加密组件用于如密码加密

3.shiro登录认证

例子1:最简单的登录认证

pom

<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.11</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>commons-logging</groupId>
  <artifactId>commons-logging</artifactId>
  <version>1.1.3</version>
</dependency>
<!--shiro认证-->
<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-core</artifactId>
  <version>1.2.2</version>
</dependency>

java代码 

public class LoginTest {

    /**
     * 模拟一个最简单的登录
     */
    @Test
    public void TestAuthentication(){
        //1.创建一个SecurityManager类
        DefaultSecurityManager  defaultSecurityManager=new DefaultSecurityManager();
        //创建一个realm域,存储用户账号信息
        SimpleAccountRealm simpleAccountRealm=new SimpleAccountRealm();
        simpleAccountRealm.addAccount("jzk","1234");
        //设置到SecurityManager
        defaultSecurityManager.setRealm(simpleAccountRealm);
        //2.创建一个主体
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        Subject subject=SecurityUtils.getSubject();
        //3.创建一个登陆token
        UsernamePasswordToken token=new UsernamePasswordToken("jzk","12345");
        //登陆
        try{
            subject.login(token);
            //查看登录是否成功
            System.out.println("login is "+subject.isAuthenticated());
        }
        catch (UnknownAccountException ex){
            System.out.println("登录账号出错");
        }
        catch (IncorrectCredentialsException e){
            System.out.println("密码错误");
        }
        catch (Exception e){
            System.out.println("其他异常");
        }



    }
}

代码导读

(1)创建一个SecurityManager类,并设置Realm域的账号密码

(2)在SecurityUtils类帮助下撞见一个主体

(3)主体通过一个token进行登录操作

 (4)UnknownAccountException 等异常抛出会反应登录的各种情况

 

例子2:最简单的授权

授权和登录验证类似,需要在realm里增加用户的角色数据

Java代码

/**
 * 模拟一个最简单的登录授权
 */
@Test
public void testRoleAuthentication(){
    //1.创建一个SecurityManager类
    DefaultSecurityManager  defaultSecurityManager=new DefaultSecurityManager();
    //创建一个realm域,存储用户账号信息
    SimpleAccountRealm simpleAccountRealm=new SimpleAccountRealm();
    //角色组
    String roles[]=new String[]{"admin","approcve","user"};
    simpleAccountRealm.addAccount("jzk","1234",roles);
    //设置到SecurityManager
    defaultSecurityManager.setRealm(simpleAccountRealm);
    //2.创建一个主体
    SecurityUtils.setSecurityManager(defaultSecurityManager);
    Subject subject=SecurityUtils.getSubject();
    //3.创建一个登陆token
    UsernamePasswordToken token=new UsernamePasswordToken("jzk","1234");
    //登陆
    try{
        subject.login(token);
        //查看登录是否成功
        System.out.println("login is "+subject.isAuthenticated());

        //==============是否有相关权限
        //权限role验证
        //单个角色
        subject.checkRole("admin");//注意这个不会返回bool,而是通过UnauthorizedException异常
         System.out.println("role is "+subject.isAuthenticated());
        //多个角色
        subject.checkRoles("admin","user");
        System.out.println("roles is "+subject.isAuthenticated());
        //退出
        subject.logout();
    }
    catch (UnknownAccountException ex){
        System.out.println("登录账号出错");
    }
    catch (IncorrectCredentialsException e){
        System.out.println("密码错误");
    }
    catch (UnauthorizedException e){
        System.out.println("没有相关权限");
    }
    catch (Exception e){
        System.out.println("其他异常");
    }

}

代码导读:

1)在realm里增加角色数据

2)登录后用subject.checkRole或者subject.checkRoles检查是否有一个或者多个权限

4.reaml域详解

reaml是存储用户登录及权限的数据,有很多reaml可以实现

SimpleAccountRealm
//创建一个realm域,存储用户账号信息
SimpleAccountRealm simpleAccountRealm=new SimpleAccountRealm();
//角色组
String roles[]=new String[]{"admin","approcve","user"};
simpleAccountRealm.addAccount("jzk","1234",roles);
IniAccountRealm保存到ini文件里
JDBCAccountRealm保存到数据库里
CustAccountRealm自定义

 

(1)ini域

ini文件

用户
[users]
#用户zhang的密码是123,此用户具有role1和role2两个角色
zhang=123,role1,role2
wang=123,role2
 
#权限
[roles]
#角色role1对资源user拥有create、update权限
role1=user:create,user:update
#角色role2对资源user拥有create、delete权限
role2=user:create,user:delete
#角色role3对资源user拥有create权限
role3=user:create
 

java代码:

/**
 * ini领域
 */
@Test
public void iniAuthentication(){
    //1.创建一个SecurityManager类
    DefaultSecurityManager  defaultSecurityManager=new DefaultSecurityManager();
    //创建一个inirealm域,存储用户账号信息
    IniRealm iniRealm=new IniRealm("classpath:ini/user.ini");

    //设置到SecurityManager
    defaultSecurityManager.setRealm(iniRealm);
    //2.创建一个主体
    SecurityUtils.setSecurityManager(defaultSecurityManager);
    Subject subject=SecurityUtils.getSubject();
    //3.创建一个登陆token
    UsernamePasswordToken token=new UsernamePasswordToken("jzk","12345");
    //登陆
    try{
        subject.login(token);
        //查看登录是否成功
        System.out.println("login is "+subject.isAuthenticated());

        //==============是否有相关权限
        //权限role验证
        //单个角色
        subject.checkRole("admin");//注意这个不会返回bool,而是通过UnauthorizedException异常
        System.out.println("admin role is "+subject.isAuthenticated());
        //多个角色
        subject.checkRoles("admin","user");
        System.out.println("roles admin,user is "+subject.isAuthenticated());

        //退出
        subject.logout();
    }
    catch (UnknownAccountException ex){
        System.out.println("登录账号出错");
    }
    catch (IncorrectCredentialsException e){
        System.out.println("密码错误");
    }
    catch (UnauthorizedException e){
        System.out.println("没有相关权限");
    }
    catch (Exception e){
        System.out.println("其他异常");
    }

}

ini概述

[main]
#提供了对根对象 securityManager 及其依赖的配置
securityManager=org.apache.shiro.mgt.DefaultSecurityManager
…………
securityManager.realms=$jdbcRealm
 
[users]
#提供了对用户/密码及其角色的配置,用户名=密码,角色 1,角色 2
username=password,role1,role2
 
 
[roles]
#提供了角色及权限之间关系的配置,角色=权限 1,权限 2
role1=permission1,permission2
 
 
[urls]
#用于 web,提供了对 web url 拦截相关的配置,url=拦截器[参数],拦截器
/index.html = anon
/admin/** = authc, roles[admin], perms["permission1"]
 

4)权限验证

[users]
jzk=12345,admin,user
jth=111,admin
[roles]
admin=user:delete,user:update
user=user:login,user:logout
/**
 * ini领域权限验证
 */
@Test
public void iniPpermission(){
    //1.创建一个SecurityManager类
    DefaultSecurityManager  defaultSecurityManager=new DefaultSecurityManager();
    //创建一个inirealm域,存储用户账号信息
    IniRealm iniRealm=new IniRealm("classpath:ini/user.ini");

    //设置到SecurityManager
    defaultSecurityManager.setRealm(iniRealm);
    //2.创建一个主体
    SecurityUtils.setSecurityManager(defaultSecurityManager);
    Subject subject=SecurityUtils.getSubject();
    //3.创建一个登陆token
    UsernamePasswordToken token=new UsernamePasswordToken("jzk","12345");
    //登陆
    try{
        subject.login(token);
        //查看登录是否成功
        System.out.println("login is "+subject.isAuthenticated());

        //==============是否有相关权限
        //权限role验证
        //单个角色
        subject.checkRole("admin");//注意这个不会返回bool,而是通过UnauthorizedException异常
        System.out.println("admin role is "+subject.isAuthenticated());
        //多个角色
        subject.checkRoles("admin","user");
        System.out.println("roles admin,user is "+subject.isAuthenticated());

        //权限验证
            System.out.println("=====权限验证====");
            System.out.println("======subject.isAuthenticated判断权限,无抛出异常UnauthorizedException==============");
            //单个权限
            subject.checkPermission("user:create");
            System.out.println("Permission[user:create]  is "+subject.isAuthenticated());
            //多个权限
            subject.checkPermissions("user:create","user:update");
            System.out.println("Permissions[user:create,user:update]  is "+subject.isAuthenticated());


            System.out.println("======subject.isPermitted判断权限==============");
            //使用isPermission判断权限,返回boolean,boolean[]值不会抛出异常
            //单个权限
            System.out.println("Permission[user:create]  is "+subject.isPermitted("user:create"));
            //多个权限
            System.out.println("Permissions[user:create,user:update]  is "+subject.isPermittedAll("user:create","user:update"));

        //退出
        subject.logout();
    }
    catch (UnknownAccountException ex){
        System.out.println("登录账号出错");
    }
    catch (IncorrectCredentialsException e){
        System.out.println("密码错误");
    }
    catch (UnauthorizedException e){
        System.out.println("没有相关权限");
    }
    catch (Exception e){
        System.out.println("其他异常");
    }

}

(5)JDBCAccountRealm验证域

realm数据来自数据库

pom

<dependency>

<groupId>org.apache.shiro</groupId>

<artifactId>shiro-core</artifactId>

<version>1.5.1</version>

</dependency>

<dependency>

<groupId>mysql</groupId>

<artifactId>mysql-connector-java</artifactId>

<version>8.0.19</version>

</dependency>

<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->

<dependency>

<groupId>com.alibaba</groupId>

<artifactId>druid</artifactId>

<version>1.1.12</version>

</dependency>

sql

SET FOREIGN_KEY_CHECKS=0;

 

DROP TABLE IF EXISTS `users`;

CREATE TABLE `users` (

  `password` varchar(255) DEFAULT NULL,

  `password_salt` varchar(255) DEFAULT NULL,

  `username` varchar(255) NOT NULL,

  PRIMARY KEY (`username`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

 

 

-- ----------------------------

-- Table structure for user_roles

-- ----------------------------

DROP TABLE IF EXISTS `user_roles`;

CREATE TABLE `user_roles` (

  `username` varchar(255) DEFAULT NULL,

  `role_name` varchar(255) DEFAULT NULL

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

 

 

-- ----------------------------

-- Table structure for roles_permissions

-- ----------------------------

DROP TABLE IF EXISTS `roles_permissions`;

CREATE TABLE `roles_permissions` (

  `role_name` varchar(255) NOT NULL,

  `permission` varchar(255) NOT NULL,

  PRIMARY KEY (`role_name`,`permission`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

java代码

@Test
public void jdbcPpermission(){
    //1.创建一个SecurityManager类
    DefaultSecurityManager  defaultSecurityManager=new DefaultSecurityManager();
    //创建一个jdbcRealm域,存储用户账号信息
    JdbcRealm jdbcRealm=new JdbcRealm();
    DruidDataSource ds=new DruidDataSource();
    ds.setUrl("jdbc:mysql://127.0.0.1:3306/shiri?serverTimezone=UTC");
    ds.setUsername("root");
    ds.setPassword("xbsoft");
    jdbcRealm.setDataSource(ds);
    //权限查询开关,默认为false,不设置将查询不到权限
    jdbcRealm.setPermissionsLookupEnabled(true);
    //设置到SecurityManager
    defaultSecurityManager.setRealm(jdbcRealm);
    //2.创建一个主体
    SecurityUtils.setSecurityManager(defaultSecurityManager);
    Subject subject=SecurityUtils.getSubject();
    //3.创建一个登陆token
    UsernamePasswordToken token=new UsernamePasswordToken("jzk","12345");
    //登陆
    try{
        subject.login(token);
        //查看登录是否成功
        System.out.println("login is "+subject.isAuthenticated());

        //==============是否有相关权限
        //权限role验证
        //单个角色
        subject.checkRole("admin");//注意这个不会返回bool,而是通过UnauthorizedException异常
        System.out.println("admin role is "+subject.isAuthenticated());
        //多个角色
        subject.checkRoles("admin","user");
        System.out.println("roles admin,user is "+subject.isAuthenticated());

        //验证权限
        subject.checkPermission("user:update");
        System.out.println("Permission 【user:update】 is "+subject.isAuthenticated());

        //退出
        subject.logout();
    }
    catch (UnknownAccountException ex){
        System.out.println("登录账号出错");
    }
    catch (IncorrectCredentialsException e){
        System.out.println("密码错误");
    }
    catch (UnauthorizedException e){
        System.out.println("没有相关权限");
    }
    catch (Exception e){
        System.out.println("其他异常");
    }

}

 导读

为什么我们没有写sql数据,系统能读取,打开JdbcRealm

protected DataSource dataSource;
protected String authenticationQuery = "select password from users where username = ?";
protected String userRolesQuery = "select role_name from user_roles where username = ?";
protected String permissionsQuery = "select permission from roles_permissions where role_name = ?";
protected boolean permissionsLookupEnabled = false;

他已经内置了查询sql语句

 

自定义相关查询SQL语句

在我们实际应用中,基本不可能沿用内置的SQL和表,我们则可以自定义密码,角色,权限对应的SQL语句

 

/**
 * 自定义sql jdbc领域
 */
@Test
public void jdbcSQLPpermission(){
    //1.创建一个SecurityManager类
    DefaultSecurityManager  defaultSecurityManager=new DefaultSecurityManager();
    //创建一个jdbcRealm域,存储用户账号信息
    JdbcRealm jdbcRealm=new JdbcRealm();
    DruidDataSource ds=new DruidDataSource();
    ds.setUrl("jdbc:mysql://127.0.0.1:3306/shiri?serverTimezone=UTC");
    ds.setUsername("root");
    ds.setPassword("xbsoft");
    jdbcRealm.setDataSource(ds);
    //权限查询开关,默认为false,不设置将查询不到权限
    jdbcRealm.setPermissionsLookupEnabled(true);

    //=============设置自定义SQL========
    /** 系统自带,需要按照这个规律
     protected String authenticationQuery = "select password from users where username = ?";
     protected String userRolesQuery = "select role_name from user_roles where username = ?";
     protected String permissionsQuery = "select permission from roles_permissions where role_name = ?";
     */
    //登录sql
    String authenticationQuery="select pwd from xt_user where login_code=?";
    jdbcRealm.setAuthenticationQuery(authenticationQuery);

    //角色查询SQL
    String userRolesQuery="SELECT c.role_name FROM xt_user a" +
            "   INNER JOIN xt_user_role b ON a.id=b.user_id " +
            "   INNER JOIN xt_role  c ON b.role_id=c.role_id" +
            "   where  a.is_ok=1 and a.login_code=?";
    jdbcRealm.setUserRolesQuery(userRolesQuery);

    //查询权限SQL
    String permissionsQuery="select c.rec_name from xt_role a " +
            "INNER JOIN xt_role_permission b ON a.role_id=b.role_id " +
            "INNER JOIN xt_resource c ON b.rec_id=c.rec_id " +
            "where a.role_name=?";

    jdbcRealm.setPermissionsQuery(permissionsQuery);

    //设置到SecurityManager
    defaultSecurityManager.setRealm(jdbcRealm);
    //2.创建一个主体
    SecurityUtils.setSecurityManager(defaultSecurityManager);
    Subject subject=SecurityUtils.getSubject();
    //3.创建一个登陆token
    UsernamePasswordToken token=new UsernamePasswordToken("13688006635","1");
    //登陆
    try{
        subject.login(token);
        //查看登录是否成功
        System.out.println("login is "+subject.isAuthenticated());

        //==============是否有相关权限
        //权限role验证
        //单个角色
        subject.checkRole("admin");//注意这个不会返回bool,而是通过UnauthorizedException异常
        System.out.println("admin role is "+subject.isAuthenticated());
        //多个角色
        subject.checkRoles("admin","user");
        System.out.println("roles admin,user is "+subject.isAuthenticated());

        //验证权限
        subject.checkPermission("user:insert");
        System.out.println("Permission 【user:insert】 is "+subject.isAuthenticated());

        //退出
        subject.logout();
    }
    catch (UnknownAccountException ex){
        System.out.println("登录账号出错");
    }
    catch (IncorrectCredentialsException e){
        System.out.println("密码错误");
    }
    catch (UnauthorizedException e){
        System.out.println("没有相关权限");
    }
    catch (Exception e){
        System.out.println("其他异常");
    }

}

6.自定义Realm类

我们查看JDBCRealm类,其继承了AuthorizingRealm,重点的是实现两个方法,登录验证和权限信息

 

 自定义realm

package com.jyj.shiro;

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 java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/** 自定义Realm,实现自己的方法
 * @class: com.jyj.shiro.CustRealm
 * @author: jiangzengkui
 * @company: 教育家
 * @create: 2020-12-20 15:13
 * @description:
 * @updateRemark:
 */
public class CustRealm extends AuthorizingRealm {
   static  Map<String,String> data=new HashMap<>();
    static {
        data.put("jzk","123456");
        data.put("dage","1");
    }

    /**
     *
     * @param authenticationToken 登录检验
     * @return  登录成功信息
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //得到登录用户账号
        String login_code=(String)authenticationToken.getPrincipal();
        //得到密码
        String pwd=getPwd(login_code);
        SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(login_code,pwd,getName());

        return info;
    }


    /**
     * 获得授权数据
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //得到登录用户账号
        String login_code=(String) principalCollection.getPrimaryPrincipal();
        //获得角色信息
        Set<String> roles=getRole(login_code);

        //获得权限信息
        Set<String> permissions=getPermission(login_code);

        //new  SimpleAuthorizationInfo,设置相关的角色和权限数据
        SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();

        info.setRoles(roles);
        info.setStringPermissions(permissions);
        return info;
    }




    /**
     * realm需要取个名称
     * @return
     */
    @Override
    public String getName() {
        // TODO Auto-generated method stub
        return "myRealm";
    }



    /**
     *这里虚拟了从数据里通过登录账号查询密码信息
     * @return
     */
    private String getPwd(String loginCode){
        String pwd=null;
        pwd=data.get(loginCode);
        if (pwd == null) {
            throw new UnknownAccountException("No account found for user [" + loginCode + "]");
        }

        return pwd;
    }

    /**模拟从数据库
     * 通过用户账号获得角色信息
     * @param login_code
     * @return
     */
    private Set<String> getRole(String login_code) {
        Set<String> roles=new HashSet<>();
        roles.add("admin");
        roles.add("user");
        return roles;
    }

    /**
     * m模拟从数据库获得权限数据
     * @param login_code
     * @return
     */
    private Set<String> getPermission(String login_code) {
        Set<String> permission=new HashSet<>();
        permission.add("user:delete");
        permission.add("user:update");
        return permission;
    }


}

测试类

/**
 * 自定义realm域
 */
@Test
public void  customRealm(){
    //1.创建一个SecurityManager类
    DefaultSecurityManager  defaultSecurityManager=new DefaultSecurityManager();
    //创建一个jdbcRealm域,存储用户账号信息
    CustRealm custRealm=new CustRealm();//自定义

    //设置到SecurityManager
    defaultSecurityManager.setRealm(custRealm);
    //2.创建一个主体
    SecurityUtils.setSecurityManager(defaultSecurityManager);
    Subject subject=SecurityUtils.getSubject();
    //3.创建一个登陆token
    UsernamePasswordToken token=new UsernamePasswordToken("dage","1");
    //登陆
    try{
        subject.login(token);
        //查看登录是否成功
        System.out.println("login is "+subject.isAuthenticated());

        //==============是否有相关权限
        //权限role验证
        //单个角色
        subject.checkRole("admin");//注意这个不会返回bool,而是通过UnauthorizedException异常
        System.out.println("admin role is "+subject.isAuthenticated());
        //多个角色
        subject.checkRoles("admin","user");
        System.out.println("roles admin,user is "+subject.isAuthenticated());

        //验证权限
        subject.checkPermission("user:update");
        System.out.println("Permission 【user:update】 is "+subject.isAuthenticated());

        //退出
        subject.logout();
    }
    catch (UnknownAccountException ex){
        System.out.println("登录账号出错");
    }
    catch (IncorrectCredentialsException e){
        System.out.println("密码错误");
    }
    catch (UnauthorizedException e){
        System.out.println("没有相关权限");
    }
    catch (Exception e){
        System.out.println("其他异常");
    }

}

 

7.加密算法

一般我们在数据库保存的用户密码都是经过加密后的密码,所以我们想把加密后的用户信息传给shiro进行认证,就必须把该密码加密的算法、添加的盐以及散列的次数告诉shiro,因此我们需要配置realm里面的凭证匹配器credentialsMatcher,当用户将账号密码输进来时,shiro就会根据我们设置的加密规则对密码进行加密加盐,然后与realm中查询封装好的数据库数据进行比对认证

1)设置域的加密算法

HashedCredentialsMatcher matcher=new HashedCredentialsMatcher();

matcher.setHashAlgorithmName("md5");//加密算法

matcher.setHashIterations(1);//加密次数

ream.setCredentialsMatcher(matcher);

MD5算法

import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.crypto.hash.SimpleHash;

/**
 * @class: com.jyj.shiro.TestMd5
 * @author: jiangzengkui
 * @company: 教育家
 * @create: 2020-12-20 21:50
 * @description:
 * @updateRemark:
 */

public class TestMd5 {

    public static void main(String[] args) {
        String password = "123456";//要加密的字符串
        String salt = "wjw";//盐
        Integer hashIterations = 1;//散列次数
        //1.不加盐的md5
        Md5Hash md5 = new Md5Hash(password);
        System.out.println(md5.toString());


        //2.加盐的md5
        md5 = new Md5Hash(password, salt);
        System.out.println(md5.toString());

        //3.加盐再设置散列次数的md5
        md5 = new Md5Hash(password, salt, hashIterations);
        System.out.println(md5.toString());

        //4.利用SimpleHash来设置md5(上面三种都可以通过这个来设置,这里举例加盐加散列次数的)
        //第一个参数是算法名称,这里指定md5,第二个是要加密的密码,第三个参数是加盐,第四个是散列次数
        //SimpleHash("md5", password) ==    Md5Hash md5 = new Md5Hash(password);
        SimpleHash hash = new SimpleHash("md5", password, salt,hashIterations);
        System.out.println(hash.toString());
    }
}

不加盐的MD5

只需要在ream声明里加入加密算法即可,其他的不变

/**
 * 自定义realm域的加密算法,不加盐
 */
@Test
public void  customCryptNotSaltRealm(){
    //1.创建一个SecurityManager类
    DefaultSecurityManager  defaultSecurityManager=new DefaultSecurityManager();
    //创建一个jdbcRealm域,存储用户账号信息
    CustRealm custRealm=new CustRealm();//自定义域
    /**
     * 设置加密算法
     */
    //如果密码是加密
    HashedCredentialsMatcher matcher=new HashedCredentialsMatcher();
    matcher.setHashAlgorithmName("md5");//加密算法
    matcher.setHashIterations(1);//加密次数
    custRealm.setCredentialsMatcher(matcher);



    //设置到SecurityManager
    defaultSecurityManager.setRealm(custRealm);
    //2.创建一个主体
    SecurityUtils.setSecurityManager(defaultSecurityManager);
    Subject subject=SecurityUtils.getSubject();
    //3.创建一个登陆token
    UsernamePasswordToken token=new UsernamePasswordToken("jzk","123456");
    //登陆
    try{
        subject.login(token);
        //查看登录是否成功
        System.out.println("login is "+subject.isAuthenticated());

        //==============是否有相关权限
        //权限role验证
        //单个角色
        subject.checkRole("admin");//注意这个不会返回bool,而是通过UnauthorizedException异常
        System.out.println("admin role is "+subject.isAuthenticated());
        //多个角色
        subject.checkRoles("admin","user");
        System.out.println("roles admin,user is "+subject.isAuthenticated());

        //验证权限
        subject.checkPermission("user:update");
        System.out.println("Permission 【user:update】 is "+subject.isAuthenticated());

        //退出
        subject.logout();
    }
    catch (UnknownAccountException ex){
        System.out.println("登录账号出错");
    }
    catch (IncorrectCredentialsException e){
        System.out.println("密码错误");
    }
    catch (UnauthorizedException e){
        System.out.println("没有相关权限");
    }
    catch (Exception e){
        System.out.println("其他异常");
    }

}

如果用了加了盐,则在域里要把盐加入

package com.jyj.shiro;

import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/** 自定义Realm,实现自己的加密方法
 * @class: com.jyj.shiro.CustRealm
 * @author: jiangzengkui
 * @company: 教育家
 * @create: 2020-12-20 15:13
 * @description:
 * @updateRemark:
 */
public class CustCryptRealm extends AuthorizingRealm {
   static  Map<String,String> data=new HashMap<>();
    static {
        data.put("jzk","e947d7c6721c1e5c6062984dc30829af");//加了密的密码
        data.put("dage","1");
    }

    /**
     *
     * @param authenticationToken 登录检验
     * @return  登录成功信息
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //得到登录用户账号
        String login_code=(String)authenticationToken.getPrincipal();
        //得到密码
        String pwd=getPwd(login_code);
        SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(login_code,pwd,getName());
        //SimpleAuthenticationInfo必须设置加盐
        info.setCredentialsSalt(ByteSource.Util.bytes("jzk"));//必须加盐

        return info;
    }


    /**
     * 获得授权数据
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //得到登录用户账号
        String login_code=(String) principalCollection.getPrimaryPrincipal();
        //获得角色信息
        Set<String> roles=getRole(login_code);

        //获得权限信息
        Set<String> permissions=getPermission(login_code);

        //new  SimpleAuthorizationInfo,设置相关的角色和权限数据
        SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();

        info.setRoles(roles);
        info.setStringPermissions(permissions);
        return info;
    }




    /**
     * realm需要取个名称
     * @return
     */
    @Override
    public String getName() {
        // TODO Auto-generated method stub
        return "myRealm";
    }



    /**
     *这里虚拟了从数据里通过登录账号查询密码信息
     * @return
     */
    private String getPwd(String loginCode){
        String pwd=null;
        pwd=data.get(loginCode);
        if (pwd == null) {
            throw new UnknownAccountException("No account found for user [" + loginCode + "]");
        }

        return pwd;
    }

    /**模拟从数据库
     * 通过用户账号获得角色信息
     * @param login_code
     * @return
     */
    private Set<String> getRole(String login_code) {
        Set<String> roles=new HashSet<>();
        roles.add("admin");
        roles.add("user");
        return roles;
    }

    /**
     * m模拟从数据库获得权限数据
     * @param login_code
     * @return
     */
    private Set<String> getPermission(String login_code) {
        Set<String> permission=new HashSet<>();
        permission.add("user:delete");
        permission.add("user:update");
        return permission;
    }

    public static  void main(String[] args){
        String password = "123456";//要加密的字符串
        String salt = "jzk";//盐
        Integer hashIterations = 1;//散列次数
        //1.不加盐的md5
        Md5Hash md5 = new Md5Hash(password);
        System.out.println("no salt:=="+md5.toString());


        //2.加盐的md5
        md5 = new Md5Hash(password, salt);
        System.out.println("salt:=="+md5.toString());

        //3.加盐再设置散列次数的md5
        md5 = new Md5Hash(password, salt, hashIterations);
        System.out.println("salt hashIterations:=="+md5.toString());
    }


}

测试类加密设置不变

 //如果密码是加密
    HashedCredentialsMatcher matcher=new HashedCredentialsMatcher();
    matcher.setHashAlgorithmName("md5");//加密算法
    matcher.setHashIterations(1);//加密次数
    custRealm.setCredentialsMatcher(matcher);

 

JdbcRealm加密加盐

上文实现了自定义realm的加密加盐,如果直接用JdbcRealm类如何加密加盐呢

数据库表

代码

/**
 *  JdbcRealm 加密加盐的算法
 */
@Test
public void jdbcSQLCryptSaltPermission(){
    //1.创建一个SecurityManager类
    DefaultSecurityManager  defaultSecurityManager=new DefaultSecurityManager();
    //创建一个jdbcRealm域,存储用户账号信息
    JdbcRealm jdbcRealm=new JdbcRealm();
    DruidDataSource ds=new DruidDataSource();
    ds.setUrl("jdbc:mysql://127.0.0.1:3306/shiri?serverTimezone=UTC");
    ds.setUsername("root");
    ds.setPassword("xbsoft");
    jdbcRealm.setDataSource(ds);
    //权限查询开关,默认为false,不设置将查询不到权限
    jdbcRealm.setPermissionsLookupEnabled(true);

    //=============设置自定义SQL========
    /** 系统自带,需要按照这个规律
     protected String authenticationQuery = "select password from users where username = ?";
     protected String userRolesQuery = "select role_name from user_roles where username = ?";
     protected String permissionsQuery = "select permission from roles_permissions where role_name = ?";
     */
    //登录sql
    String authenticationQuery="select pwd,salt from xt_user where login_code=?";
    jdbcRealm.setAuthenticationQuery(authenticationQuery);

    //角色查询SQL
    String userRolesQuery="SELECT c.role_name FROM xt_user a" +
            "   INNER JOIN xt_user_role b ON a.id=b.user_id " +
            "   INNER JOIN xt_role  c ON b.role_id=c.role_id" +
            "   where  a.is_ok=1 and a.login_code=?";
    jdbcRealm.setUserRolesQuery(userRolesQuery);

    //查询权限SQL
    String permissionsQuery="select c.rec_name from xt_role a " +
            "INNER JOIN xt_role_permission b ON a.role_id=b.role_id " +
            "INNER JOIN xt_resource c ON b.rec_id=c.rec_id " +
            "where a.role_name=?";

    jdbcRealm.setPermissionsQuery(permissionsQuery);

    //如果密码是加密而且加盐
    HashedCredentialsMatcher matcher=new HashedCredentialsMatcher();
    matcher.setHashAlgorithmName("md5");//加密算法
    matcher.setHashIterations(1);//加密次数
    jdbcRealm.setCredentialsMatcher(matcher);
    /**
     加盐:JdbcRealm.SaltStyle.COLUMN表示从表的字段读取盐职
     String authenticationQuery="select pwd,salt from xt_user where login_code=?";
     第二个值表示从数据库字段读取盐值
     */
    jdbcRealm.setSaltStyle(JdbcRealm.SaltStyle.COLUMN);



    //设置到SecurityManager
    defaultSecurityManager.setRealm(jdbcRealm);
    //2.创建一个主体
    SecurityUtils.setSecurityManager(defaultSecurityManager);
    Subject subject=SecurityUtils.getSubject();
    //3.创建一个登陆token
    UsernamePasswordToken token=new UsernamePasswordToken("13688006635","123456");
    //登陆
    try{
        subject.login(token);
        //查看登录是否成功
        System.out.println("login is "+subject.isAuthenticated());

        //==============是否有相关权限
        //权限role验证
        //单个角色
        subject.checkRole("admin");//注意这个不会返回bool,而是通过UnauthorizedException异常
        System.out.println("admin role is "+subject.isAuthenticated());
        //多个角色
        subject.checkRoles("admin","user");
        System.out.println("roles admin,user is "+subject.isAuthenticated());

        //验证权限
        subject.checkPermission("user:insert");
        System.out.println("Permission 【user:insert】 is "+subject.isAuthenticated());

        //退出
        subject.logout();
    }
    catch (UnknownAccountException ex){
        System.out.println("登录账号出错");
    }
    catch (IncorrectCredentialsException e){
        System.out.println("密码错误");
    }
    catch (UnauthorizedException e){
        System.out.println("没有相关权限");
    }
    catch (Exception e){
        System.out.println("其他异常");
    }

}

二、权限过滤

 

 

 

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值