Springboot_Shiro

一、shiro简介

1. shiro是啥?

  • Apache Shiro 是 Java 的一个安全(权限)框架。

  • Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在 JavaSE 环境,也可以用在 JavaEE 环境。

  • Shiro 可以完成:认证、授权、加密、会话管理、与Web 集成、缓存等。

  • 深入学习:

    • 官网:http://shiro.apache.org/
    • github:https://github.com/apache/shiro

2. Shiro 基本功能

在这里插入图片描述

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

  • Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能进行什么操作,如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限

  • Session Management:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境,也可以是Web 环境的

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

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

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

  • Concurrency:Shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去

  • Testing:提供测试支持

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

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

3. 实现框架

在这里插入图片描述

  • Subject:应用代码直接交互的对象是Subject,也就是说Shiro的对外API 核心就是Subject。Subject 代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等;与Subject 的所有交互都会委托给SecurityManager;Subject 其实是一个门面,SecurityManager才是实际的执行者

  • SecurityManager:安全管理器;即所有与安全有关的操作都会与SecurityManager交互;且其管理着所有Subject;可以看出它是Shiro的核心,它负责与Shiro的其他组件进行交互,它相当于SpringMVC中DispatcherServlet的角色

  • Realm:Shiro从Realm 获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm 得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm 看成DataSource

二、入门实践

  • 查看官方文档:http://shiro.apache.org/tutorial.html

  • 官方的quickstart : https://github.com/apache/shiro/tree/master/samples/quickstart/

1.创建一个是springboot - web项目,在这里插入图片描述

  • 我的springboot 版本
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.6</version>
    </parent>

2. 导入依赖

  • shiro 依赖,
  • 数据库依赖
  • mybatis-plus依赖
  • 整合 Thymeleaf 依赖
  • loombok 依赖
  • thymeleaf 整合 shiro 依赖 ( 导入后,可以在html中使用一些shiro的方法,但是需要在前端导入命名空间,导入位置在html顶头,出现 xmlns的地方
    • xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
<!--        连接数据库-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
<!--        mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.0.5</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    <!--导入thymeleaf依赖-->
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring5</artifactId>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-java8time</artifactId>
        </dependency>
<!--        thymleaf整合 shiro-->

        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.1.0</version>
        </dependency>
<!--导入 shiro-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>
    </dependencies>

3. 编写配置类 ShiroConfig 和 自定义类 Realm

  • 第一步:创建 Realm对象(对象中包含两个操作,一是授权,二是认证
  • 第二步:创建 DafaulWebSecurityManager 容器
  • 第三步:创建 ShiroFilterFactoryBean 容器
ShiroConfig 类
package com.janson.ShiroConfig;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
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;

/**
 * @Author Janson
 * @Date 2022/4/5 16:22
 * @Version 1.0
 */
@Configuration
public class ShiroConfig {

    //ShiroFilterFactoryBean  第三步
    @Bean
    public ShiroFilterFactoryBean getshirofilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);

        //添加shiro 的内置过滤器
        /**
         *  anon : 无需认证就可以访问
         *  authc : 必须认证才能访问
         *  user : 必须拥有 记住我 功能才能用
         *  perms : 拥有对某个资源的权限才能访问
         *  role: 拥有某个角色权限才能访问
         */
        Map<String,String> filterMap = new LinkedHashMap();
        //filterMap.put("/user/add","authc");
        //filterMap.put("/user/update","authc");
        //采用通配符也可以

        //授权,正常情况下,,没有授权,就会跳转到未授权页面
        filterMap.put("/user/update","perms[user:update]");

        filterMap.put("/user/*","authc");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);

        shiroFilterFactoryBean.setLoginUrl("/toLogin");
        //未授权 跳转的页面
        shiroFilterFactoryBean.setUnauthorizedUrl("/noauth");
        return shiroFilterFactoryBean;
    }

    //DafaulWebSecurityManager  第二步
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("shiroRealm") ShiroRealm shiroRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //关联 ShiroRealm
         securityManager.setRealm(shiroRealm);
        return securityManager;
    }

    // 创建 realm 对象  1.第一步
    @Bean
    public ShiroRealm shiroRealm(){

        return new ShiroRealm();
    }
    @Bean
    public ShiroDialect getshiroDialect(){
        return new ShiroDialect();
    }

}

ShiroRealm类:
package com.janson.ShiroConfig;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.janson.entity.User;
import com.janson.mapper.UserMapper;
import org.apache.shiro.SecurityUtils;
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 org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * @Author Janson
 * @Date 2022/4/5 16:30
 * @Version 1.0
 */
public class ShiroRealm extends AuthorizingRealm {

    @Autowired
    private UserMapper userMapper;
    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行力=>授权操作");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //info.addStringPermission("user:update");
        //拿到当前登录的对象
        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 = "root";
        String password ="123456";

        User user = userMapper.selectOne(new QueryWrapper<User>().eq("username", userToken.getUsername()));

        if (user == null){
            //会抛出异常
            return null;
        }
        //下边第一个写 user,才能在其他地方获取到当前的 用户数据,如果 不写,其他地方获取的 则为 null
        return new SimpleAuthenticationInfo(user,user.getPassword(),"");
    }
}

4.登录拦截(代码见上述配置类具体过程)

5. 用户认证(代码见上述配置类具体过程)

  • anon : 无需认证就可以访问
  • authc : 必须认证才能访问
  • user : 必须拥有 记住我 功能才能用
  • perms : 拥有对某个资源的权限才能访问
  • role: 拥有某个角色权限才能访问

6.密码加密(MD5盐值加密)

SimpleHash md5 = new SimpleHash("MD5", password, username);

登录过程(MyController代码)登录、 注销很方便,详细看代码
package com.janson.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.janson.entity.User;
import com.janson.mapper.UserMapper;
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.crypto.hash.SimpleHash;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * @Author Janson
 * @Date 2022/4/5 15:42
 * @Version 1.0
 */
@Controller
public class MyController{
    @Autowired
    UserMapper userMapper;
    private static boolean isLogin = false;
    @RequestMapping({"/","/index"})
    public String  toIndex(Model model){
        Subject subject = SecurityUtils.getSubject();
        User currentUser = (User)subject.getPrincipal();
        model.addAttribute("msg","hello Shrio");
        //if (isLogin){
        //    model.addAttribute("username",currentUser.getUsername());
        //}
        return "index";
    }

    @RequestMapping("/user/add")
    public String toAdd(){
        return "user/add";
    }

    @RequestMapping("/user/update")
    public String toUpdate(Model model){
        return "user/update";
    }

    @RequestMapping("/toLogin")
    public String toLogin(){
        return "login";
    }

    @RequestMapping("/login")
    public String login(String username,String password,Model model){
    //    获取当前的用户
        Subject subject = SecurityUtils.getSubject();
        //密码加密
        SimpleHash md5 = new SimpleHash("MD5", password, username);
        System.out.println(md5);
        //    封装用户的登录数据
        UsernamePasswordToken token = new UsernamePasswordToken(username,md5.toString());

        //执行登录,没有异常就登录成功
        try {
            subject.login(token);
            Session session = subject.getSession();
            isLogin = true;
            User user = userMapper.selectOne(new QueryWrapper<User>().eq("username", username));
            session.setAttribute("loginUser",user);

            return "redirect:/index";

        }catch (UnknownAccountException e){
            model.addAttribute("msg","用户名错误");
            return "login";
        }catch (IncorrectCredentialsException e){
            model.addAttribute("msg","密码错误");
            return "login";
        }
    }

    @RequestMapping("/logout")
    public String logout(){
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return "index";
    }

    @RequestMapping("/noauth")
    @ResponseBody
    public String unauthorized(){

        return "未经授权无法访问该页面";
    }
}

7.用户授权(在ShiroRealm中有详细过程)

8.整合 thymeleaf (导入依赖,前边导入依赖已完成)

三、其他代码

1.entity 类(此处使用了 loombok简化开发)
package com.janson.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;

/**
 * @Author Janson
 * @Date 2022/4/6 9:55
 * @Version 1.0
 */
@Data
@NoArgsConstructor`在这里插入代码片`
@AllArgsConstructor
@TableName("login")
public class User {
    @TableId(type = IdType.AUTO)
    private String id;

    private String username;
    private String password;
    private String email;
    private int deleted;
    private int version;
    private Date createTime;
    private Date updateTime;
    private String perms;
}

2. mapper(使用mybatis-plu简化)
package com.janson.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.janson.entity.User;
import org.springframework.stereotype.Repository;

/**
 * @Author Janson
 * @Date 2022/4/6 9:59
 * @Version 1.0
 */
@Repository
public interface UserMapper extends BaseMapper<User> {
}

四、前端代码(thymeleaf模板开发)

1.login.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
</head>
<body>
<div>
    <p th:text="${msg}" style="color: red"></p>
    <form th:action="@{/login}">
        <p>用户名:<input type="text" name="username"></p>
        <p>密码:<input type="password" name="password"></p>
        <button type="submit">登录</button>
    </form>
</div>

</body>
</html>

2. index.html ()

shiro:hasPermission=“user:update” ====》 shiro整合了 thymeleaf,此处用于判断用户权限
必须导入命令空间: xmlns:shiro=“http://www.thymeleaf.org/thymeleaf-extras-shiro”

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
            xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro"
>
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
<h3>首页</h3>
<p th:text="${msg}"><p/>
<div>
    <div style="float: right">
<!--        <p th:text="${username}"></p>-->
        <div th:if="${session.loginUser!=null}">
        <!--            通过session获取用户信息,注意:方法名后边不能有: ()-->
            <p th:text="${session.loginUser.getUsername}"></p>

        </div>

        <div  th:if="${session.loginUser==null}" >
             <a th:href="@{/toLogin}">登录</a>
        </div>
        <div  th:if="${session.loginUser!=null}">
            <a th:href="@{/logout}">注销</a>
        </div>
    </div>

    <div shiro:hasPermission="user:add">

        <a th:href="@{/user/add}">add</a>

    </div>
    <div shiro:hasPermission="user:update">
        <a th:href="@{/user/update}">update</a>

    </div>

</div>
</body>
</html>

3.user目录下的add.html和update.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1> add </h1>

</body>
</html>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>update</title>
</head>
<body>
<h1>update</h1>
</body>
</html>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Janson666

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值