Shiro入门

Shiro的使用

今天学习了Shiro框架,感觉不是很懂,有点懵,写篇随笔记录一下

1. Shiro三个核心组件

1.1 Subject
  Subject:即“当前操作用发户”。但是,在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。但考虑到大多数目的和用途,你可以把它认为是Shiro的“用户”概念。Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。

1.2 SecurityManager
  SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。

1.3 Realm
  Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。
  从这个意义上讲,Realm实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权。配置多个Realm是可以的,但是至少需要一个。
  Shiro内置了可以连接大量安全数据源(又名目录)的Realm,如LDAP、关系数据库(JDBC)、类似INI的文本配置资源以及属性文件等。如果缺省的Realm不能满足需求,你还可以插入代表自定义数据源的自己的Realm实现。

2. Shiro相关类介绍

(1)Authentication 认证 ---- 用户登录
(2)Authorization 授权 --- 用户具有哪些权限
(3)Cryptography 安全数据加密
(4)Session Management 会话管理
(5)Web Integration web系统集成
(6)Interations 集成其它应用,spring、缓存框架

3. Shiro 特点

(1)易于理解的 Java Security API;
(2)简单的身份认证(登录),支持多种数据源(LDAP,JDBC,Kerberos,ActiveDirectory 等);
(3)对角色的简单的签权(访问控制),支持细粒度的签权;
(4)支持一级缓存,以提升应用程序的性能;
(5)内置的基于 POJO 企业会话管理,适用于 Web 以及非 Web 的环境;
(6)异构客户端会话访问;
(7)非常简单的加密 API;
(8)不跟任何的框架或者容器捆绑,可以独立运行

一. 新建SpringBoot项目,导入相关依赖,

<!--
            Subject 用户
            SecurityManager 管理所有用户
            Realm 连接数据
-->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.4.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.github.theborakompanioni/thymeleaf-extras-shiro -->
<dependency>
    <groupId>com.github.theborakompanioni</groupId>
    <artifactId>thymeleaf-extras-shiro</artifactId>
    <version>2.0.0</version>
</dependency>

二. 创建数据库、实体类以及mapper配置文件(为了方便直接使用上一篇整合Myabtis的数据库和文件)

1.创建数据库表

DROP DATABASE IF EXISTS mybatis;
CREATE DATABASE mybatis;

DROP TABLE  IF EXISTS USER;
CREATE TABLE USER(
	id INT PRIMARY KEY AUTO_INCREMENT,
	username VARCHAR(32) NOT NULL COMMENT '姓名',
	PASSWORD VARCHAR(32) NOT NULL COMMENT '密码',
    perms 	VARCHAR(32) NOT NULL COMMENT '权限参数'
)ENGINE=INNODB AUTO_INCREMENT=1 CHARSET=utf8;

INSERT INTO USER(username,PASSWORD,perms) VALUES("niubi","123456","user-add");
INSERT INTO USER(username,PASSWORD,perms) VALUES("wocao","123456","user-update");
INSERT INTO USER(username,PASSWORD) VALUES("Java","123456");
INSERT INTO USER(username,PASSWORD) VALUES("python","123456");
INSERT INTO USER(username,PASSWORD) VALUES("C","123456");

2.创建实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Integer id;
    private String username;
    private String password;
    private String perms;
}

3.创建UserMapper接口


@Mapper//表示本类是一个Mybatis的mapper
@Repository//不加也可以,但是Controller会报错,但是不影响正常运行
public interface UserMapper {
    //根据用户名查询用户
    User findUserByUserName(String username);
}

4.编写UserMapper.xml配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gaoteng.mapper.UserMapper">

    <select id="findUserByUserName" resultType="User">
        select * from user where username = #{username}
    </select>
</mapper>

5.编写application.properties配置文件


# 配置数据源
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&serverTimezone=UTC&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 开启别名映射
mybatis.type-aliases-package=com.gaoteng.pojo
# mapper的路径 就是xml配置文件中的<mappers>标签
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml

三. 编写前端页面

首页

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
 	 <a th:href="@{/user/toLogin}" shiro:guest="">登录</a>
    <a th:href="@{/user/logout}" style="float: right">注销用户</a>
    <div shiro:hasPermission="user:add">
        <a th:href="@{/user/add}">add</a><br>
    </div>
    <div shiro:hasPermission="user:update">
        <a th:href="@{/user/update}">update</a>
    </div>
</body>
</html>

add.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>add</h1>
</body>
</html>

login.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h2>登录</h2>
    <p th:text="${msg}"></p>
    <hr>
    <form method="post" th:action="@{/login}">
        用户名:<input type="text" name="username"><br>
        密码:<input type="password" name="password"><br>
        <input type="submit" value="提交">
    </form>
</body>
</html>

upodate.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>update</h1>
</body>
</html>

四.新建config包,创建ShiroConfig配置文件和UserRealm类

package com.gaoteng.config;

import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

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

@Configuration
public class ShiroConfig {
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
        ShiroFilterFactoryBean bean= new ShiroFilterFactoryBean();
        bean.setSecurityManager(securityManager);
        //添加Shiro的内置过滤器
        /*
        *   anon:无需认证就能访问
        *   authc:必须认证了才能访问
        *   user:必须拥有 记住我 功能才能访问
        *   perms:拥有对某个资源的权限才能访问
        *   roles:拥有某个角色权限才能访问
        *	logout:注销
        * */
        //拦截
        Map<String,String> filterMap = new LinkedHashMap<>();
        filterMap.put("/user/add","authc");
        filterMap.put("/user/update","authc");
        //注销  
        filterMap.put("/user/logout","logout");
        //授权  正常情况下,没有授权就会跳转到未授权页面
        filterMap.put("/user/add","perms[user:add]");
        filterMap.put("/user/update","perms[user:update]");
        bean.setFilterChainDefinitionMap(filterMap);
        //设置登录的请求
        bean.setLoginUrl("/user/toLogin");
        //设置未授权页面
        bean.setUnauthorizedUrl("/noauth");
        return bean;

    }

    @Bean
    @Qualifier("securityManager")
    public DefaultWebSecurityManager defaultWebSecurityManager(@Autowired UserRealm userRealm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //关联UserRealm
        securityManager.setRealm(userRealm);
        return securityManager;
    }

    @Bean
    public UserRealm userRealm(){
        return new UserRealm();
    }
     @Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }
}

package com.gaoteng.config;


import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;


public class UserRealm extends AuthorizingRealm {
     @Autowired
    UserMapper userMapper;//这里为了方便演示,没有编写Service层
    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("授权");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //给所有用户增加user:add权限
        //info.addStringPermission("user:add");

        //拿到当前登录的用户对象
        Subject subject = SecurityUtils.getSubject();
        User currentUser = (User) subject.getPrincipal();
        //设置当前用户的权限
        info.addStringPermission(currentUser.getPerms());
        return info;
    }
    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("认证");
        //用户名和密码
        UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;
        //获取用户名
        String username = userToken.getUsername();
        //根据用户名查询用户是否存在
        User user = userMapper.findUserByUserName(username);
        if(user==null){
            return null;//抛出UnknownAccountException异常
        }
        //密码是由Shiro来处理
        return new SimpleAuthenticationInfo(user,user.getPassword(),"");
    }
}

四. 编写Cotroller

package com.gaoteng.controller;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class TestController {
    @RequestMapping({"/","/index","/index.html"})
    public String index(Model model){
        model.addAttribute("msg","niubi");
        return "index";
    }
    @RequestMapping("/user/add")
    public String add(){
        return "add";
    }
    @RequestMapping("/user/update")
    public String update(){
        return "update";
    }
    @RequestMapping("/user/toLogin")
    public String toLogin(){
        return "login";
    }
    @RequestMapping("/login")
    public String login(String username,String password,Model model){
        //获取当前用户
        Subject subject = SecurityUtils.getSubject();
        //封装用户的登录数据
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);

        try {
            //执行登陆方法
            subject.login(token);
            return "redirect:index";
        } catch (UnknownAccountException e) {
                  model.addAttribute("msg","用户名不存在");
                  return "login";
        }catch (IncorrectCredentialsException e){
                model.addAttribute("msg","密码错误");
                return "login";
        }
    }
     @RequestMapping("/noauth")
    @ResponseBody
    public String unauthorized(){
        return "未经授权,无法访问";
    }
    //注销 如果注销操作中需要额外的操作时,可以自己编写Controller处理,如果只需要简单的注销功能,
    //只需要在Shiro的内置过滤器中添加类似于filterMap.put("/user/logout","logout");的代码
   /* @RequestMapping("/user/logout")
    public String logout(){
         Subject subject = SecurityUtils.getSubject();
    	subject.logout();
    	return "login";
    }*/
}

因为运行要演示的图片太多,所以这里就不演示了.

不过我现在只知道怎么使用,并不了解原理.如果想了解原理的话,可以请阅读其它文章.

本篇随笔借鉴了以下文章或视频:[权限框架之Shiro详解(非原创)](https://www.cnblogs.com/WUXIAOCHANG/p/10886534.html)

shiro常用标签

springboot整合shiro -shiro介绍(一)
【狂神说Java】SpringBoot最新教程IDEA版通俗易懂

原文地址:https://www.cnblogs.com/gttttttt/p/13060452.html