06SpringBoot 集合Shiro

Shiro安全框架

  1. Apache Shiro是一个Java的安全(权限)框架
  2. Shiro可以非常容易的开发出足够好的应用,不仅可以应用在JavaSE的环境,也可以用在JavaEE的环境
  3. Shiro可以完成认证,授权,加密,会话管理,Web集成,缓存等
  4. 下载地址:Shiro官方描述

框架结构
在这里插入图片描述Subject:当前用户

Shiro SecurityManager:作用是管理所有的用户,也就是管理Subjet

Realm:保证数据访问控制,相当于数据层,与数据打交道

实战测试

准备工作

准备工作包括创建实体类,创建Mapper,对应的xml文件,连接数据库,配置mybatis基本连接属性等基础操作。
在这里插入图片描述

User实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String name;
    private String password;
    private String perms;
}

Mapper层以及对应的xml文件
记得加上对应的注解

@Mapper
@Repository
public interface UserMapper {
    User queryBy(String name);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wang.mapper.UserMapper">
    <select id="queryBy" parameterType="String" resultType="User">
        select * from user where name=#{name};
    </select>
</mapper>

Service层以及其实现类

public interface UserService {
    User queryBy(String name);
}
@Service
public class UserServiceImpl implements UserService{
    @Autowired
    UserMapper userMapper;
    @Override
    public User queryBy(String name) {
        return userMapper.queryBy(name);
    }
}

新建yml文件,配置参数

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf-8
    username: root
    password: 123456
mybatis:
  type-aliases-package: com.wang.pojo
  mapper-locations: classpath:mapper/*.xml
1.依赖导入
<!--        thymeleaf 和shrio的整合-->
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>
        <!--        thymeleaf-->
 <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-java8time</artifactId>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring5</artifactId>
        </dependency>
 <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.1</version>
        </dependency>
         <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.0</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.21</version>
        </dependency>
2.自定义UserRealm
public Class UserRealm extends AuthorizingRealm{
 @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行了授权操作");
        }
        @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("执行了认证操作");
        }
}
3.配置ShiroConfig

配置ShiroConfig有三步:

  1. 创建Realm,返回自己定义的UserRealm
  2. 创建默认的DefaultWebSecurityManager
  3. 创建ShiroFilterFactoryBean
  4. 加入注解交由SpringBoot接管

(从下往上会顺一点)

@Configuration
public class ShiroConfig {
    //第三步:ShiroFilterFactoryBean
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
            ShiroFilterFactoryBean bean=new ShiroFilterFactoryBean();
            //管理setSecurityManager
            bean.setSecurityManager(securityManager);
           return bean;
}
//第二步:DefaultWebSecurityManager
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
        DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
        //与自定义的Realm建立联系
        securityManager.setRealm(userRealm);
        return securityManager;
    }
 //第一步:创建realm,返回自定义realm,
    @Bean
    public UserRealm userRealm(){
        return new UserRealm();
    }
}
4.编写页面的跳转

在这里插入图片描述

@Controller
public class PageController {
    @RequestMapping({"/","/index"})
    public String index(Model model){
        model.addAttribute("message","Hello,Shiro");
        return "index";
    }

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

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

测试各个页面跳转是否正常

5.实现登陆的拦截

我们需要设计这样一个需求:如果用户未登录,点击访问各个页面,则会被Shiro拦截无法访问,必须要先实现用户的登录。
可以在shiroFilterFactoryBean()方法中设置Shiro的内置过滤器

  //添加Shiro的内置过滤器
            Map<String,String> map=new LinkedHashMap<>();
 //认证
            map.put("/add","authc");
            map.put("/update","authc");
            bean.setFilterChainDefinitionMap(map);
//设置登录的请求
            bean.setLoginUrl("/toLogin");

测试结果:
未登录状态时,点击add页面,无法进入,随后弹出登录的页面。
在这里插入图片描述

6.实现用户认证

为了便于方便理解,将用户认证的代码放于controller层。用户进行登陆的时候,若用户名或密码输入错误,则会出现相应的提示信息,并且要求要重新输入。

 @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 "index";
        }
        catch (UnknownAccountException e){
            model.addAttribute("message","用户名输入不正确");
            return "login";
        }
        catch (IncorrectCredentialsException e){
            model.addAttribute("message","密码输入不正确");
            return "login";
        }
    }

在自定义的UserRealm的doGetAuthenticationInfo()方法中获取数据库中传入的用户名和密码。请记得要注入UserService

 @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("执行了认证操作");
        //转型
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        //连接真实数据库
        User user = userService.queryBy(token.getUsername());
        //匹配数据库中的数据
        if (user==null) {
            //抛出异常:UnknownAccountException
            return null;
        }
        //shiro自己做相关密码的匹配工作
        return new SimpleAuthenticationInfo(user, user.getPassword(), "");
    }

测试:
在这里插入图片描述在这里插入图片描述
测试完成,符合要求。

7.请求授权的实现

向LinkedHashMap中添加权限的限制

//授权
            map.put("/add","perms[user:add]");
            map.put("/update","perms[user:update]");

向controller层添加若没有权限访问页面时返回的情况

 @RequestMapping("/noPrivate")
    @ResponseBody
    public String noAuthorized(){
        return "没有权限访问进行此操作";
    }

向shiroFilterFactoryBean()方法添加

 //设置无访问权限后要跳转的页面
            bean.setUnauthorizedUrl("/noPrivate");

这样访问页面的时候若该用户没有对应的权限的时候,就无法访问,会出现以上的提示信息了。

在自定义的UserRealm的doGetAuthorizationInfo()方法中完善授权操作的代码

  1. 向认证的方法中拿到当前登录的用户
  2. 获取你存储的principal,可以进行强制转型
  3. 向数据库中读取当前用户的相关权限
  4. 注意返回info
 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行了授权操作");

        SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
        info.addStringPermission("user:add");
        info.addStringPermission("user:update");
        //拿到当前登录这个对象
        Subject subject = SecurityUtils.getSubject();
        //强制转型
        User currentUser = (User) subject.getPrincipal();
        //设置当前用户权限
        info.addStringPermission(currentUser.getPerms());
        System.out.println(currentUser);
        //注意返回info
        return info;

    }

测试:
测试结果显示,当用户只有访问add页面的权限时,不能访问update的页面,反之亦然。

8.整合Thymeleaf

需求:整合,用于thymeleaf和shiro标签整合使用

在ShiroConfig类里添加

@Bean
    public ShiroDialect getShiroDialect(){
        return  new ShiroDialect();
    }

需求:当前用户只能访问add页面时,当他登陆后,只出现进入add的页面按钮,没有出现进入其他页面的按钮。若已登录,则不会再出现登录按钮。

先在doGetAuthenticationInfo()的方法中编写获取登录的loginUser.session,然后再传给前端进行对比。

 Subject currentSubject = SecurityUtils.getSubject();
        Session session = currentSubject.getSession();
        session.setAttribute("loginUser",user);

index页面的修改,导入shiro与thymeleaf结合的命名空间

在这里插入图片描述

完整配置代码如下

UserRealm:

public class UserRealm extends AuthorizingRealm {
    @Autowired
    UserService userService;
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行了授权操作");

        SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
//        info.addStringPermission("user:add");
//        info.addStringPermission("user:update");
        //拿到当前登录这个对象
        Subject subject = SecurityUtils.getSubject();
        //强制转型
        User currentUser = (User) subject.getPrincipal();
        //设置当前用户权限
        info.addStringPermission(currentUser.getPerms());
        System.out.println(currentUser);
        //注意返回info
        return info;

    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("执行了认证操作");
        //转型
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        //连接真实数据库
        User user = userService.queryBy(token.getUsername());
        //匹配数据库中的数据
        if (user==null) {
            //抛出异常:UnknownAccountException
            return null;
        }
        Subject currentSubject = SecurityUtils.getSubject();
        Session session = currentSubject.getSession();
        session.setAttribute("loginUser",user);
        //shiro自己做相关密码的匹配工作
        return new SimpleAuthenticationInfo(user, user.getPassword(), "");
    }
}

ShiroConfig:

package com.wang.config;

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;

@Configuration
public class ShiroConfig {
    //第三步:ShiroFilterFactoryBean
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
            ShiroFilterFactoryBean bean=new ShiroFilterFactoryBean();
            //管理setSecurityManager
            bean.setSecurityManager(securityManager);

            //添加Shiro的内置过滤器
            Map<String,String> map=new LinkedHashMap<>();
            //授权
            map.put("/add","perms[user:add]");
            map.put("/update","perms[user:update]");

            //认证
            map.put("/add","authc");
            map.put("/update","authc");
            bean.setFilterChainDefinitionMap(map);
            //设置登录的请求
            bean.setLoginUrl("/toLogin");
            //设置无访问权限后要跳转的页面
            bean.setUnauthorizedUrl("/noPrivate");
    return bean;
}

//第二步:DefaultWebSecurityManager
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
        DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
        //与自定义的Realm建立联系
        securityManager.setRealm(userRealm);
        return securityManager;
    }

    //第一步:创建realm,返回自定义realm,
    @Bean
    public UserRealm userRealm(){
        return new UserRealm();
    }

    @Bean
    public ShiroDialect getShiroDialect(){
        return  new ShiroDialect();
    }
}

至此Shiro的集合基础 基本完成

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值