springboot 整合shiro 实例讲解全部源码奉上

1,shiro基本框架

1,什么是shiro

shro是一款主流的安全框架,不依赖任何容器,可以运行在javaEE和javaSE项目中,它的主要作用是,对访问系统的用户进行身份验证,授权,会话管理,加密等操作

shiro就是用来解决安全管理的系统化框架。

2,shiro的核心组件

用户,角色,权限之间的关系。

1,UsernamePasswordToken, shiro 用来封装用户登录信息,使用用户的登录信息来创建令牌token,

2, SecurityManager , shiro 的核心部分,负责安全认证和授权。

3,suject , shiro的一个抽象概念,包含了用户信息

4,Realm , 开发者自定义的模块,根据项目的需求,验证和授权的逻辑全部写在Realm中。

5,Authenticationnfo, 用户的角色信息集合,认证时使用。

6, AuthorzationInfo, 角色的权限信息集合,授权时使用

7,DefaultWebSecurityManager , 安全管理器,开发者自定义的Realm需要注入到 DefaultWebSecurityManager 进行管理才能生效

8,ShiroFilterFactoryBean ,过滤器工厂,Shiro的基本运行机制是由开发者来制定的,Shiro去执行,具体的执行操作就是有ShiroFilterFactoryBean 创建一个个Filter对象来完成。

shiro运行原理

2,springboot整合shiro

项目目录

1,简单集成 shiro,导入依赖

<!--使用lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!--        导入shiro的依赖-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.5.3</version>
        </dependency>

<!--        连接数据的依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

<!--        使用mybatis-plus的依赖-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.0.5</version>
        </dependency>   
        
        

创建一个实体类Account类并且指定表的名字

package com.zzuli.entity;


import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

//使用lombok自动生成 set和get方法
@Data
@TableName("testshiro")
public class Account {

    private Integer id;
    private String username;
    private String password;
    private String perms;
    private  String role;


}

完成数据库,连接配置后,然后,创建mapper接口并且测试是否连接数据库成功

package com.zzuli.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.zzuli.entity.Account;
import org.springframework.stereotype.Repository;

//这个接口要继承 baseMapper
      //这个注解加不加都行,如果不加的话,使用 AUtowire 的时候就会爆红,加了就不会爆红了
                   //但是爆红不影响程序的执行
@Repository
public interface AccountMapper extends BaseMapper<Account> {

}

直接创建一个测试类

package com.zzuli.mapper;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;


@SpringBootTest  //单元测试  是否 mybatis连接是否成功
class AccountMapperTest {
    //依赖注入
    //因为ioc时动态代理的所以会爆红
    //只要在对应的mapper接口上加入  @Repository  就不会报错。
    @Resource
    private AccountMapper accountMapper;


    @Test
    void  test1(){
        accountMapper.selectList(null).forEach(System.out::println);
    }

}

查看输出结果

创建service层和serviceImpl层创建业务逻辑方法并且实现

IAccountService接口

package com.zzuli.service;

import com.zzuli.entity.Account;
import org.springframework.stereotype.Repository;


public interface IAccountService {
    //创建一个方法用来判断用户名是否存在

    Account findUsername(String username);

}

AccountServiceImpl实现类

package com.zzuli.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zzuli.entity.Account;
import com.zzuli.mapper.AccountMapper;
import com.zzuli.service.IAccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.sql.Wrapper;

@Service //表示这个类为服务层
public class AccountServiceImpl implements IAccountService {

    //依赖注入
    @Autowired
    private AccountMapper mapper;


    @Override
    public Account findUsername(String username) {

        //创建要给模板条件对象
        QueryWrapper wrapper=new QueryWrapper<Account>();

        //加入条件
        wrapper.eq("username",username);
        //通过名字查找对象,所以时条件查找

        return  mapper.selectOne(wrapper);

    }
}

测试逻辑是否成功

AccountServiceImplTest

package com.zzuli.service.impl;

import com.zzuli.entity.Account;
import com.zzuli.service.IAccountService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;

import static org.junit.jupiter.api.Assertions.*;
//加入测试注解
@SpringBootTest
class AccountServiceImplTest {
    //依赖注入

    @Autowired
    private IAccountService iAccountService;

    @Test
    void findUsername() {


        Account account =iAccountService.findUsername("唐僧");

        System.out.println(account);

    }
}

查询测试结果

2,自定义shiro过滤器

创建一个普通类AccountRealm 并且需要继承AuthorizingRealm抽象类,并且需要实现两个方法,两个方法分别用来做认证和授权

package com.zzuli.realm;

import com.zzuli.entity.Account;
import com.zzuli.service.IAccountService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;

public class AccountRealm  extends AuthorizingRealm {

//    依赖注入
    @Autowired
    private IAccountService service;

    /*
    *
    * 这个方法时用来做授权的
    * */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }



    /*
     *
     * 这个方法时用来做认证的
     * */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

        //authenticationToken 参数已经包含请求用户的信息
        //创建一个usernamePasswordToken用来封装用户的信息,向下转型

        UsernamePasswordToken token= (UsernamePasswordToken) authenticationToken;

        //从封装好的token中拿取请求用户的信息
        Account account =service.findUsername(token.getUsername());

        //判断用户请求的信息是否存在数据库中
        if (account!=null){

            //程序走到这一步说明,用户发送的请求体正确,然后判断用户挈带的密码是否和数据库保持一致
            //这个类可以将参数和用户请求中的参数进行对比
            return   new SimpleAuthenticationInfo(account,account.getPassword(),getName());
        }

        //account不存在
        return null;
    }
}

创建一个配置类ShiroConfig

配置类,逻辑结构图

代码实现

package com.zzuli.config;

import com.zzuli.realm.AccountRealm;
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;

@Configuration  //表明这个类为配置类
public class ShiroConfig {

    //将AccountReaml类注入到IOc容器中 , 在ioc容器中 名称默认为accountRealm
    @Bean
    public AccountRealm accountRealm(){

        return new AccountRealm();
    }
    //将shiro中的组件DefaultWebSecurityManager也交给ioc容器管理
    //@Qualifier(”名称“)  将从ioc总调取 名称为accountRealm的实体类
    //将创建的AccountRealm实体类注入到DefaultWebSecurityManager
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("accountRealm") AccountRealm accountRealm){

        //创建DefaultWebSecurityManager组件对象
        DefaultWebSecurityManager manager=new DefaultWebSecurityManager();
        //将AccountRealm注入到DefaultWebSecurityManager组件中
        manager.setRealm(accountRealm);
        return manager;
    }
    //将shiro中的shiro工厂组件ShiroFilterFactoryBean注入到 ioc容器中
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
        //生成一个工程组件对象
        ShiroFilterFactoryBean factoryBean=new ShiroFilterFactoryBean();
        //将DefaultWebSecurityManager组件注入到 ShiroFilterFactoryBean中
        factoryBean.setSecurityManager(defaultWebSecurityManager);
        return new ShiroFilterFactoryBean();
    }


}

3,编写认证和授权规则

认证过滤器

  1. anon: 无需认证
  2. authc: 必须认证
  3. authcBasic 需要通过HTTPBasic认证
  4. user : 不一定通过认证,只要曾经被shiro记录即可, 例如: 记住我 选项

授权过滤器

  1. perms: 必须拥有某个权限才能访问
  2. role : 必须拥有某个角色才能访问
  3. port: 请求的端口必须时指定的端口号才可以
  4. rest: 规定请求方式, RESTful, POST, PUT, GET, DELETE
  5. ssl: 必须时安全的URL请求,协议要求是HTTPS

创建三个页面,main.html, manager.html ,administator.html

访问权限如下

  • 必须登录之后才能访问main.html
  • 当前用户必须拥有,manager授权才能访问,manager.html
  • 当前用户必须拥有,administator角色才能访问,adiministartor.html

在配置类中给访问路径添加访问权限设置

package com.zzuli.config;

import com.zzuli.realm.AccountRealm;
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.HashMap;

@Configuration  //表明这个类为配置类
public class ShiroConfig {

    //将AccountReaml类注入到IOc容器中 , 在ioc容器中 名称默认为accountRealm
    @Bean
    public AccountRealm accountRealm(){

        return new AccountRealm();
    }
    //将shiro中的组件DefaultWebSecurityManager也交给ioc容器管理
    //@Qualifier(”名称“)  将从ioc总调取 名称为accountRealm的实体类
    //将创建的AccountRealm实体类注入到DefaultWebSecurityManager
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("accountRealm") AccountRealm accountRealm){

        //创建DefaultWebSecurityManager组件对象
        DefaultWebSecurityManager manager=new DefaultWebSecurityManager();
        //将AccountRealm注入到DefaultWebSecurityManager组件中
        manager.setRealm(accountRealm);
        return manager;
    }
    //将shiro中的shiro工厂组件ShiroFilterFactoryBean注入到 ioc容器中
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
        //生成一个工程组件对象
        ShiroFilterFactoryBean factoryBean=new ShiroFilterFactoryBean();
        //将DefaultWebSecurityManager组件注入到 ShiroFilterFactoryBean中
        factoryBean.setSecurityManager(defaultWebSecurityManager);
        /*
        * 权限设置,使用map结合来完成
        *
        * */

        //创建一个hashmap集合
        HashMap<String,String> map =new HashMap<>();
        //在map集合中  key用来表示请求的路径     value  表示拦截

        //对main.html 页面进行拦截, 表示之后登录认证之后才能访问  main.html页面
        map.put("/main" , "authc");

        //访问manage.heml页面的时候,必须拥有,manager权限之后才能访问,可以有多个权限
        map.put("/manage","perms[manage]");

        //当访问administator页面的时候,只有拥有 admin角色的用户才能访问
        map.put("/administator","roles[admin]");

        //将map集合装入到factory的过滤集合map中
        factoryBean.setFilterChainDefinitionMap(map);
        return factoryBean;
    }


}

配置application.property来完成视图解析器配置

#配置数据库的连接信息
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=root


#使用mybatis-plusd打印日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.slf4j.Slf4jImpl


#配置视图解析器
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html

创建控制器类

package com.zzuli.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;


@Controller   //表明这个类为控制器层
public class AccountController {

    //映射路径,并且绑定参数使用  @PathVariable  指定参数之间的映射
    @GetMapping("/{urlname}")
    public String redirect(@PathVariable("urlname") String urlname){

        return urlname;
    }

}

创建四个html页面

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

<h1>index</h1>

<a href="/main">main</a><br> |
<a href="/manage">manage</a><br> |
<a href="/administator">administator</a><br> |
</body>
</html>

当点击超链接的时候就会报以下异常

异常解决方案,在每一个html页面中加入一行代码

主要目的就是屏蔽 favicon.ico

<link rel="icon" href="data:image/ico;base64,aWNv">

设置默认登录页面路径

package com.zzuli.config;

import com.zzuli.realm.AccountRealm;
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.HashMap;

@Configuration  //表明这个类为配置类
public class ShiroConfig {

    //将AccountReaml类注入到IOc容器中 , 在ioc容器中 名称默认为accountRealm
    @Bean
    public AccountRealm accountRealm(){

        return new AccountRealm();
    }
    //将shiro中的组件DefaultWebSecurityManager也交给ioc容器管理
    //@Qualifier(”名称“)  将从ioc总调取 名称为accountRealm的实体类
    //将创建的AccountRealm实体类注入到DefaultWebSecurityManager
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("accountRealm") AccountRealm accountRealm){

        //创建DefaultWebSecurityManager组件对象
        DefaultWebSecurityManager manager=new DefaultWebSecurityManager();
        //将AccountRealm注入到DefaultWebSecurityManager组件中
        manager.setRealm(accountRealm);
        return manager;
    }
    //将shiro中的shiro工厂组件ShiroFilterFactoryBean注入到 ioc容器中
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
        //生成一个工程组件对象
        ShiroFilterFactoryBean factoryBean=new ShiroFilterFactoryBean();
        //将DefaultWebSecurityManager组件注入到 ShiroFilterFactoryBean中
        factoryBean.setSecurityManager(defaultWebSecurityManager);
        /*
        * 权限设置,使用map结合来完成
        *
        * */

        //创建一个hashmap集合
        HashMap<String,String> map =new HashMap<>();
        //在map集合中  key用来表示请求的路径     value  表示拦截

        //对main.html 页面进行拦截, 表示之后登录认证之后才能访问  main.html页面
        map.put("/main" , "authc");

        //访问manage.heml页面的时候,必须拥有,manager权限之后才能访问,可以有多个权限
        map.put("/manage","perms[manage]");

        //当访问administator页面的时候,只有拥有 admin角色的用户才能访问
        map.put("/administator","roles[admin]");

        //将map集合装入到factory的过滤集合map中
        factoryBean.setFilterChainDefinitionMap(map);

        /*
        * 设置请求的登录页面路径,当用户没有权限是默认跳转到这个路径
        * */

        factoryBean.setLoginUrl("/login");
        return factoryBean;
    }

}

创建一个login.html页面

<!DOCTYPE html>
<!--使用thymeleaf语法-->
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="shortcut icon" href="#"/>
</head>
<body>
<form  action="/login">
    <table>
<!--        接收后代model传入的值-->
        <span th:text="${error}" style="color: crimson"></span>
        <tr>
            <td>用户名</td>
            <td><input type="text" name="username"></td>
        </tr>
        <tr>
            <td>密码</td>
            <td><input type="password" name="password"></td>
        </tr>
        <tr>
            <td>
                <input type="submit" value="登录">
            </td>
        </tr>
    </table>
</form>
</body>
</html>

创建一个控制器类,接收用户的请求参数并且对参数交给shiro进行验证

使用shiro验证代码逻辑

package com.zzuli.controller;

import com.zzuli.entity.Account;
import com.zzuli.service.IAccountService;
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.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.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;


@Controller   //表明这个类为控制器层
public class AccountController {

    //依赖注入
    @Autowired
    private IAccountService iAccountService;


    //映射路径,并且绑定参数使用  @PathVariable  指定参数之间的映射 将请求路径上的参数映射到方法的参数上
    @GetMapping("/{urlname}")
    public String redirect(@PathVariable("urlname") String urlname){

        return urlname;
    }




//    添加一个验证登录的方法,接收用户的参数
    @GetMapping("/login")

    public String loginReaquest(String username, String password , Model model){
        //创建一个shiro的subject组件对象,用来验证用户传递过来的信息,subject包含用户的数据库库信息
        Subject subject =SecurityUtils.getSubject();
        //获取封装用户的userpasswordToken组件对象,将用户传递的信息封装到token里面
        UsernamePasswordToken token=new UsernamePasswordToken(username,password);
        //将封装的用户传递过来的信息   传送给   suject  进行验证  使用login()
        //使用login方法的时候,程序会将数据交给 我们创建的AccountRealm类进行处理
        //从AccountRealm实现的认证方法来看如果执行login方法失败的时候就会抛出异常,所以我们需要将这个方法进行捕获
        //使用model来传递错误的信息给前端
        try {

            subject.login(token);
            //程序走到这个一步,说明信息验证成功,跳转到主页面
            return "index";
        }catch (UnknownAccountException e){

            e.printStackTrace();
            //打印错误信息
            model.addAttribute("error","用户名错误");
            //返回登录页面
            return "login";
        }catch (IncorrectCredentialsException e){

            e.printStackTrace();
            //打印错误信息
            model.addAttribute("error","密码错误");
            //返回登录页面
            return "login";
        }

    }
}

认证结果

当我们点击manage时

4,完成授权操作

AccountRealm类进行添加认证逻辑的业务代码

package com.zzuli.realm;

import com.zzuli.entity.Account;
import com.zzuli.service.IAccountService;
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;

import java.util.HashSet;
import java.util.Set;

public class AccountRealm  extends AuthorizingRealm {

//    依赖注入
    @Autowired
    private IAccountService service;

    /*
    *
    * 这个方法时用来做授权的
    * */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

        /*
          1,  获取当前用户的信息,已经认证过的信息
        */

        //获取包含用户信息的sunject对象
        Subject subject =SecurityUtils.getSubject();
        //获取当前对象
        Account account=(Account)subject.getPrincipal();

          /*
          2, 设置角色
        */

        //创建一个set集合,因为它的成员时不可重复的
        Set<String> roles=new HashSet<>();
        //给set集合增加成员,就是将account的属性role,或者说是表中的role字段,进行角色添加到set集合
        //如果表中的role比较多的时候可以多个添加角色
        roles.add(account.getRole());

        //AuthorizationInfo是shiro组件的角色权限集合
        //SimpleAuthorizationInfo是AuthorizationInfo的子类
        //这一步是设置角色
        SimpleAuthorizationInfo info=new SimpleAuthorizationInfo(roles);

         /*
          3, 设置权限

        */
        info.addStringPermission(account.getPerms());
        return info;
    }



    /*
     *
     * 这个方法时用来做认证的
     * */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

        //authenticationToken 参数已经包含请求用户的信息
        //创建一个usernamePasswordToken用来封装用户的信息,向下转型

        UsernamePasswordToken token= (UsernamePasswordToken) authenticationToken;

        //从封装好的token中拿取请求用户的信息
        Account account =service.findUsername(token.getUsername());

        //判断用户请求的信息是否存在数据库中
        if (account!=null){

            //程序走到这一步说明,用户发送的请求体正确,然后判断用户挈带的密码是否和数据库保持一致
            //这个类可以将参数和用户请求中的参数进行对比,并且将查询到的对象传递进去给 Pricipal参数
            return   new SimpleAuthenticationInfo(account,account.getPassword(),getName());
        }

        //account不存在
        return null;
    }
}

代码验证

数据库对比

5,代码改进,当用户点击到没有权限的页面,为定向打开一个页面

首先在配置类ShiroConfig中,设置,当用户访问没有权限的页面时,给他指定固定的访问路径/unauth

package com.zzuli.config;

import com.zzuli.realm.AccountRealm;
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.HashMap;

@Configuration  //表明这个类为配置类
public class ShiroConfig {

    //将AccountReaml类注入到IOc容器中 , 在ioc容器中 名称默认为accountRealm
    @Bean
    public AccountRealm accountRealm(){

        return new AccountRealm();
    }
    //将shiro中的组件DefaultWebSecurityManager也交给ioc容器管理
    //@Qualifier(”名称“)  将从ioc总调取 名称为accountRealm的实体类
    //将创建的AccountRealm实体类注入到DefaultWebSecurityManager
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("accountRealm") AccountRealm accountRealm){

        //创建DefaultWebSecurityManager组件对象
        DefaultWebSecurityManager manager=new DefaultWebSecurityManager();
        //将AccountRealm注入到DefaultWebSecurityManager组件中
        manager.setRealm(accountRealm);
        return manager;
    }
    //将shiro中的shiro工厂组件ShiroFilterFactoryBean注入到 ioc容器中
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
        //生成一个工程组件对象
        ShiroFilterFactoryBean factoryBean=new ShiroFilterFactoryBean();
        //将DefaultWebSecurityManager组件注入到 ShiroFilterFactoryBean中
        factoryBean.setSecurityManager(defaultWebSecurityManager);
        /*
        * 权限设置,使用map结合来完成
        *
        * */

        //创建一个hashmap集合
        HashMap<String,String> map =new HashMap<>();
        //在map集合中  key用来表示请求的路径     value  表示拦截

        //对main.html 页面进行拦截, 表示之后登录认证之后才能访问  main.html页面
        map.put("/main" , "authc");

        //访问manage.heml页面的时候,必须拥有,manager权限之后才能访问,可以有多个权限
        //设置权限为managa字段才可以访问,和数据库对应。
        map.put("/manage","perms[manage]");

        //当访问administator页面的时候,只有拥有 admin角色的用户才能访问
        //设置角色字符串为admin字段,和数据库对应。
        map.put("/administator","roles[admin]");

        //将map集合装入到factory的过滤集合map中
        factoryBean.setFilterChainDefinitionMap(map);

        /*
        * 设置请求的登录页面路径,请求认证的页面
        * */

        factoryBean.setLoginUrl("/login");

        /*
        *设置一个无权限的错误页面,当用户访问自己没有权限的页面时候,就会自动访问这个路径  /unauth
        * */

        factoryBean.setUnauthorizedUrl("/unauth");

        return factoryBean;
    }

}

在控制器类中设置一个方法用来处理/unauth 路径请求

package com.zzuli.controller;

import com.zzuli.entity.Account;
import com.zzuli.service.IAccountService;
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.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.*;


@Controller   //表明这个类为控制器层
public class AccountController {

    //依赖注入
    @Autowired
    private IAccountService iAccountService;


    //映射路径,并且绑定参数使用  @PathVariable  指定参数之间的映射 将请求路径上的参数映射到方法的参数上
    @GetMapping("/{urlname}")
    public String redirect(@PathVariable("urlname") String urlname){

        return urlname;
    }




//    添加一个验证登录的方法,接收用户的参数
    @GetMapping("/login")
    public String loginReaquest(String username, String password , Model model){
        //创建一个shiro的subject组件对象,用来验证用户传递过来的信息,subject包含用户的数据库库信息
        Subject subject =SecurityUtils.getSubject();
        //获取封装用户的userpasswordToken组件对象,将用户传递的信息封装到token里面
        UsernamePasswordToken token=new UsernamePasswordToken(username,password);
        //将封装的用户传递过来的信息   传送给   suject  进行验证  使用login()
        //使用login方法的时候,程序会将数据交给 我们创建的AccountRealm类进行处理
        //从AccountRealm实现的认证方法来看如果执行login方法失败的时候就会抛出异常,所以我们需要将这个方法进行捕获
        //
        try {

            subject.login(token);
            //程序走到这个一步,说明信息验证成功,跳转到主页面
            return "index";
        }catch (UnknownAccountException e){

            e.printStackTrace();
            //打印错误信息
            model.addAttribute("error","用户名错误");
            //返回登录页面
            return "login";
        }catch (IncorrectCredentialsException e){

            e.printStackTrace();
            //打印错误信息
            model.addAttribute("error","密码错误");
            //返回登录页面
            return "login";
        }

    }


    //创建一个方法,用来返回错误信息。处理当用处访问没有权限的页面的时候,所访问的路径/unauth,
    //返回值是一个json对象
    @GetMapping("/unauth")
    @ResponseBody
    public String unauthen(){
        return "你访问的页面没有权限";
    }



}

5,给用户添加一个欢迎页面和退出操作。

添加一个用户欢迎功能,其实就是将用户的信息放入到session中,在控制器类中的登录方法中,等用户通过认证之后,会将用户信息放入到shiro框架组件中的subject组件中,我们可以直接从sunject组件中获取用户信息,并且创建session

package com.zzuli.controller;

import com.zzuli.entity.Account;
import com.zzuli.service.IAccountService;
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.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.*;


@Controller   //表明这个类为控制器层
public class AccountController {

    //依赖注入
    @Autowired
    private IAccountService iAccountService;


    //映射路径,并且绑定参数使用  @PathVariable  指定参数之间的映射 将请求路径上的参数映射到方法的参数上
    @GetMapping("/{urlname}")
    public String redirect(@PathVariable("urlname") String urlname){

        return urlname;
    }




//    添加一个验证登录的方法,接收用户的参数
    @GetMapping("/login")
    public String loginReaquest(String username, String password , Model model){
        //创建一个shiro的subject组件对象,用来验证用户传递过来的信息,subject包含用户的数据库库信息
        Subject subject =SecurityUtils.getSubject();
        //获取封装用户的userpasswordToken组件对象,将用户传递的信息封装到token里面
        UsernamePasswordToken token=new UsernamePasswordToken(username,password);
        //将封装的用户传递过来的信息   传送给   suject  进行验证  使用login()
        //使用login方法的时候,程序会将数据交给 我们创建的AccountRealm类进行处理
        //从AccountRealm实现的认证方法来看如果执行login方法失败的时候就会抛出异常,所以我们需要将这个方法进行捕获
        //
        try {

            subject.login(token);
            //程序走到这个一步,说明信息验证成功,这个时候需要给用户创建一个session用来保存用户的信息

            //程序走到这一步,说明用户的信息已经放入到subject组件中了,我们可以直接从sunject组件中拿到存储的用户信息
            Account account=(Account)subject.getPrincipal();
            //将用户存放在session领域中,subje组件可以获取session
            subject.getSession().setAttribute("account",account);

            return "index";
        }catch (UnknownAccountException e){

            e.printStackTrace();
            //打印错误信息
            model.addAttribute("error","用户名错误");
            //返回登录页面
            return "login";
        }catch (IncorrectCredentialsException e){

            e.printStackTrace();
            //打印错误信息
            model.addAttribute("error","密码错误");
            //返回登录页面
            return "login";
        }

    }


    //创建一个方法,用来返回错误信息。处理当用处访问没有权限的页面的时候,所访问的路径/unauth,
    //返回值是一个json对象
    @GetMapping("/unauth")
    @ResponseBody
    public String unauthen(){
        return "你访问的页面没有权限";
    }

}

编写index.html页面

有报错不影响正常程序的执行

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="icon" href="data:image/ico;base64,aWNv">
</head>
<body>

<h1>index</h1>

<!--判断session的成员是否存在,其实时判断用户是否登录-->
<div th:if="${session.account != null}">

    <!--完成用户欢迎页面-->
    <span th:text="${session.account.username}+'欢迎回来'"></span>

</div>

<a href="/main">main</a><br> |
<a href="/manage">manage</a><br> |
<a href="/administator">administator</a><br> |
</body>
</html>

添加一个用户退出功能,其实业务逻辑就是一个简单的session销毁

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="icon" href="data:image/ico;base64,aWNv">
</head>
<body>

<h1>index</h1>

<!--判断session的成员是否存在,其实时判断用户是否登录-->
<div th:if="${session.account != null}">

    <!--完成用户欢迎页面-->
    <span th:text="${session.account.username}+'欢迎回来'"></span>
    <!--完成退出登录 真实的业务逻辑就是将session销毁 -->
    <!--创建一个请求路径主要目的就是,然后交给控制器来处理,处理器创建方法来销毁session-->
    <a href="/exit">点我退出</a>

</div>

<a href="/main">main</a><br> |
<a href="/manage">manage</a><br> |
<a href="/administator">administator</a><br> |
</body>
</html>

在控制器AccountController中创建一个方法用来实现业务逻辑,目的时销毁session

package com.zzuli.controller;

import com.zzuli.entity.Account;
import com.zzuli.service.IAccountService;
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.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.*;


@Controller   //表明这个类为控制器层
public class AccountController {

    //依赖注入
    @Autowired
    private IAccountService iAccountService;


    //映射路径,并且绑定参数使用  @PathVariable  指定参数之间的映射 将请求路径上的参数映射到方法的参数上
    @GetMapping("/{urlname}")
    public String redirect(@PathVariable("urlname") String urlname){

        return urlname;
    }




//    添加一个验证登录的方法,接收用户的参数
    @GetMapping("/login")
    public String loginReaquest(String username, String password , Model model){
        //创建一个shiro的subject组件对象,用来验证用户传递过来的信息,subject包含用户的数据库库信息
        Subject subject =SecurityUtils.getSubject();
        //获取封装用户的userpasswordToken组件对象,将用户传递的信息封装到token里面
        UsernamePasswordToken token=new UsernamePasswordToken(username,password);
        //将封装的用户传递过来的信息   传送给   suject  进行验证  使用login()
        //使用login方法的时候,程序会将数据交给 我们创建的AccountRealm类进行处理
        //从AccountRealm实现的认证方法来看如果执行login方法失败的时候就会抛出异常,所以我们需要将这个方法进行捕获
        //
        try {

            subject.login(token);
            //程序走到这个一步,说明信息验证成功,这个时候需要给用户创建一个session用来保存用户的信息

            //程序走到这一步,说明用户的信息已经放入到subject组件中了,我们可以直接从sunject组件中拿到存储的用户信息
            Account account=(Account)subject.getPrincipal();
            //将用户存放在session领域中,subje组件可以获取session
            subject.getSession().setAttribute("account",account);

            return "index";
        }catch (UnknownAccountException e){

            e.printStackTrace();
            //打印错误信息
            model.addAttribute("error","用户名错误");
            //返回登录页面
            return "login";
        }catch (IncorrectCredentialsException e){

            e.printStackTrace();
            //打印错误信息
            model.addAttribute("error","密码错误");
            //返回登录页面
            return "login";
        }

    }


    //创建一个方法,用来返回错误信息。处理当用处访问没有权限的页面的时候,所访问的路径/unauth,
    //返回值是一个json对象
    @GetMapping("/unauth")
    @ResponseBody
    public String unauthen(){
        return "你访问的页面没有权限";
    }



    //创建一个方法,用来完成请求路径/exit的请求,其主要目的就是完成session的销毁
    @GetMapping("/exit")
    public String exit(){
        //获取用户所有信息的集合组件, subject
        Subject subject=SecurityUtils.getSubject();
        //销毁session
        subject.logout();
        return "login";
    } 
}

结果演示

6,实现当一个用户登陆时,他没有权限的页面不会显示。

1,导入 shiro 整合 thymeleaf的依赖

<!--        shiro 整合 thymeleaf-->
        <dependency>

            <groupId>com.github.theborakompanioni</groupId>

            <artifactId>thymeleaf-extras-shiro</artifactId>

            <version>2.0.0</version>

        </dependency>

2,在ShiroConfig配置类中,添加 shiroDialec方言,使用实体bean交给ioc管理

package com.zzuli.config;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.zzuli.realm.AccountRealm;
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.HashMap;

@Configuration  //表明这个类为配置类
public class ShiroConfig {

    //使用bean注解将shiroDialect交给ioc管理
    @Bean
    public ShiroDialect shiroDialect(){
        return new ShiroDialect();
    }




    //将AccountReaml类注入到IOc容器中 , 在ioc容器中 名称默认为accountRealm
    @Bean
    public AccountRealm accountRealm(){

        return new AccountRealm();
    }
    //将shiro中的组件DefaultWebSecurityManager也交给ioc容器管理
    //@Qualifier(”名称“)  将从ioc总调取 名称为accountRealm的实体类
    //将创建的AccountRealm实体类注入到DefaultWebSecurityManager
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("accountRealm") AccountRealm accountRealm){

        //创建DefaultWebSecurityManager组件对象
        DefaultWebSecurityManager manager=new DefaultWebSecurityManager();
        //将AccountRealm注入到DefaultWebSecurityManager组件中
        manager.setRealm(accountRealm);
        return manager;
    }
    //将shiro中的shiro工厂组件ShiroFilterFactoryBean注入到 ioc容器中
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
        //生成一个工程组件对象
        ShiroFilterFactoryBean factoryBean=new ShiroFilterFactoryBean();
        //将DefaultWebSecurityManager组件注入到 ShiroFilterFactoryBean中
        factoryBean.setSecurityManager(defaultWebSecurityManager);
        /*
        * 权限设置,使用map结合来完成
        *
        * */

        //创建一个hashmap集合
        HashMap<String,String> map =new HashMap<>();
        //在map集合中  key用来表示请求的路径     value  表示拦截

        //对main.html 页面进行拦截, 表示之后登录认证之后才能访问  main.html页面
        map.put("/main" , "authc");

        //访问manage.heml页面的时候,必须拥有,manager权限之后才能访问,可以有多个权限
        //设置权限为managa字段才可以访问,和数据库对应。
        map.put("/manage","perms[manage]");

        //当访问administator页面的时候,只有拥有 admin角色的用户才能访问
        //设置角色字符串为admin字段,和数据库对应。
        map.put("/administator","roles[admin]");

        //将map集合装入到factory的过滤集合map中
        factoryBean.setFilterChainDefinitionMap(map);

        /*
        * 设置请求的登录页面路径,请求认证的页面
        * */

        factoryBean.setLoginUrl("/login");

        /*
        *设置一个无权限的错误页面,当用户访问自己没有权限的页面时候,就会自动访问这个路径  /unauth
        * */

        factoryBean.setUnauthorizedUrl("/unauth");

        return factoryBean;
    }

}

1,导入shiro语法在html页面上结合thyemeleaf一起使用

使用语法进行权限判断,如果当前用户没有这个权限就不会显示路径。

<!DOCTYPE html>
<!--导入shiro和 thymeleaf-->
<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</title>
    <link rel="icon" href="data:image/ico;base64,aWNv">
</head>
<body>
<h1>index</h1>
<!--判断session的成员是否存在,其实时判断用户是否登录-->
<div th:if="${session.account != null}">
    <!--完成用户欢迎页面-->
    <span th:text="${session.account.username}+'欢迎回来'"></span>
    <!--完成退出登录 真实的业务逻辑就是将session销毁 -->
    <!--创建一个请求路径主要目的就是,然后交给控制器来处理,处理器创建方法来销毁session-->
    <a href="/exit">点我退出</a>
</div>
<a href="/main">main</a><br>
<!--判断当前用户是由具有manager权限-->
<div shiro:hasPermission="manage">
    <a href="/manage">manage</a><br>
</div>
<!--判断当前用户是由具有admin角色-->
<div shiro:hasRole="admin">
    <a href="/administator">administator</a><br>
</div>
</body>
</html>

4,功能展示

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值