SpringBoot整合Shiro权限管理(小白必备)

1 篇文章 0 订阅
1 篇文章 0 订阅

SpringBoot整合Shiro权限管理(小白必备)

  • 简介
    最近在做项目中遇到了权限分配这一块,所以自学了下shiro,分享下自己在使用过程中遇到的问题,解决方法,还有一个demo,废话不多说,我们直接进入正题。

1.pom文件引入依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.hui</groupId>
    <artifactId>springboot-shiro-test</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-shiro-test</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.3.0.RELEASE</spring-boot.version>
        <thymeleaf.version>3.0.9.RELEASE</thymeleaf.version>
        <thymeleaf-layout-dialect.version>2.2.2</thymeleaf-layout-dialect.version>
    </properties>

    <dependencies>
        <!-- thymel对shiro的扩展坐标 thymeleaf需要3.0以上 -->
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>
        <!--导入配置文件处理器,配置文件进行绑定有提示-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <!--shiro和spring整合-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.1</version>
        </dependency>
        <!-- 日志记录-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <!--连接池Druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.8</version>
        </dependency>
        <!-- 模板引擎thymeleaf-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <!--springboot启动项-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- mybatis整合springboot-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.2</version>
        </dependency>
        <!--  mysql连接驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- 测试-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.3.0.RELEASE</version>
                <configuration>
                    <mainClass>com.hui.SpringbootShiroTestApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

2.配置数据库连接:(application.yml)

server:
  port: 8080
  servlet:
    context-path: /shiro
spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://localhost:3306/myschool?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver
    #   数据源其他配置 springboot没有这些自动配置,需要引入
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true
    #   配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
    filters: stat,wall
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
  thymeleaf:
    prefix: classpath:/templates/
    check-template-location: true
    suffix: .html
    encoding: UTF-8
    content-type: text/html
    mode: HTML5
    cache: false
mybatis:
  type-aliases-package: com.hui.pojo
  mapper-locations: classpath:mybatis/mapper/*.xml

spring boot中druid连接池没有默认的这些自动装配属性,需要自己手动注入。
编写DruidConfig类:

@Configuration
public class DruidConfig {

    @ConfigurationProperties(prefix = "spring.datasource")
    @Bean
    public DataSource druid() {
        return new DruidDataSource();
    }

}

3.自定义ShiroConfig类:

package com.hui.config;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
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 getShiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
        /*
        anon:无需认证可以访问
        authc:必须认证才能访问
        user:必须拥有 记住我 功能才能用
        perms:拥有对某个资源的权限才能访问
        role:拥有某个角色权限才能访问
         */
        //登录拦截
        Map<String, String> filterMap = new LinkedHashMap<>();

        //用户进入登录界面,无需认证
        filterMap.put("/", "anon");
        filterMap.put("/login", "anon");
        filterMap.put("/tologin", "anon");
        //用户进入系统,必须进行登录验证
        filterMap.put("/sys/*", "authc");
        //角色拦截
        filterMap.put("/sys/admin", "authc,roles[admin]");
        filterMap.put("/sys/user", "authc,roles[user]");
        filterMap.put("/sys/company", "authc,roles[company]");
        //权限拦截  设置admin下的所有方法都需要增删改查权限
        filterMap.put("/sys/admin/*", "perms[admin:add,admin:update,admin:delete,admin:find]");
        //设置user下的add,update,delete需要特定权限
        filterMap.put("/sys/user/find", "perms[user:find]");
        filterMap.put("/sys/user/delete", "perms[user:delete]");
        filterMap.put("/sys/user/add", "perms[user:add]");
        filterMap.put("/sys/user/update", "perms[user:update]");

        //设置登出
        filterMap.put("/logout", "logout");

        //设置admin下的某个方法需要某个权限
        // filterMap.put("/sys/admin/find", "perms[admin:find]");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
        //设置登录请求
        shiroFilterFactoryBean.setLoginUrl("/login");
        //设置401(无权限界面)
        shiroFilterFactoryBean.setUnauthorizedUrl("/noauth");
        return shiroFilterFactoryBean;
    }

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

    //创建 realm对象,需要自定义:1
    @Bean("userRealm")
    public UserRealm getUserRealm() {
        return new UserRealm();
    }

    /**
     * 配置shiro和thymeleaf整合
     *
     * @return
     */
    @Bean(name = "shiroDialect")
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }
}

4.自定义UserRealm:

package com.hui.config;

import com.hui.pojo.Account;
import com.hui.pojo.Role;
import com.hui.service.UserService;
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.session.Session;
import org.apache.shiro.session.mgt.SimpleSession;
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;

/**
 * 继承AuthorizingRealm
 */
public class UserRealm extends AuthorizingRealm {

    @Autowired
    UserService userService;

    /**
     * 授权
     *
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行了==》》授权doGetAuthorizationInfo");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //获取到当前用户
        Account account = (Account) principalCollection.getPrimaryPrincipal();

        //赋予用户角色
        Role role = userService.findRoleByAccountName(account.getUsername());
        Set<String> roleSet = new HashSet<String>();
        roleSet.add(role.getRoleType());
        info.setRoles(roleSet);
        //赋予用户权限
        Set<String> permSet = new HashSet<String>();
        permSet = userService.findAccountPerById(account.getId());
        System.out.println("账户权限:" + permSet);
        info.setStringPermissions(permSet);
        return info;
    }

    /**
     * 认证
     *
     * @param
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("执行了==》》doGetAuthenticationInfo");
        UsernamePasswordToken userToken = (UsernamePasswordToken) token;
        //用户名,密码,数据库中取
        Account account = userService.findAccountByName(userToken.getUsername());
        if (account == null) {
            return null;//用户名不存在
        }

        //获取session
        Subject currentSubject = SecurityUtils.getSubject();
        Session session = currentSubject.getSession();
        session.setAttribute("loginUser",account);

        //可以加密:md5,md5盐值加密
        //密码认证 shiro做   加密密码    这里传入数据库的密码,会自动判断
        return new SimpleAuthenticationInfo(account, account.getPassword(), this.getName());
    }
}

5.编写Mapper持久层:

package com.hui.mapper;

import com.hui.pojo.Account;
import com.hui.pojo.Role;

import java.util.Set;

public interface UserMapper {
    /**
     * 登录验证 根据名称查找账号
     *
     * @param username
     * @return
     */
    Account findAccountByName(String username);
    /**
     * 根据名称查找账号角色
     *
     * @param username
     * @return
     */
    Role findRoleByAccountName(String username);
    /**
     * 根据账号编号查找账号权限
     *
     * @param accountId
     * @return set集合
     */
    Set<String> findAccountPerById(Integer accountId);
}

6.编写登录的Controller:

package com.hui.controller;

import com.hui.pojo.Account;
import com.hui.pojo.Role;
import com.hui.service.UserService;
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.authz.annotation.RequiresRoles;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.annotation.Resource;

@Controller
public class LoginController {

    @Resource
    private UserService userService;

    @GetMapping({"/login", "/"})
    public String showIndex() {
        return "login";
    }

    @PostMapping(value = {"/tologin"})
    public String tologin(Account account, Model model) {
        System.out.println("当前登录账号:" + account);
        //1.获取当前用户
        Subject subject = SecurityUtils.getSubject();
        //2.封装用户登录数据
        UsernamePasswordToken token = new UsernamePasswordToken(account.getUsername(), account.getPassword());
        try {
            subject.login(token);//执行登录,没有异常,说明ok
            return "redirect:/sys/showview";
        } catch (UnknownAccountException ex) {//用户名不存在
            model.addAttribute("msg", "用户名错误");
            return "login";
        } catch (IncorrectCredentialsException ex) {
            model.addAttribute("msg", "密码错误");
            return "login";
        }
    }

    @RequestMapping("/sys/showview")
    public String showView() {
        //1.获取当前用户
        Subject subject = SecurityUtils.getSubject();
        Account account = (Account) subject.getPrincipal();
        //根据角色进入不同的界面
        Role role = userService.findRoleByAccountName(account.getUsername());
        if ("admin".equals(role.getRoleType())) {
            return "view/admin";
        } else if ("user".equals(role.getRoleType())) {
            return "view/user";
        } else if ("company".equals(role.getRoleType())) {
            return "view/company";
        } else {
            return "";
        }
    }

    /**
     * 无权限错误界面
     *
     * @return
     */
    @RequestMapping("/noauth")
    public String noauth() {
        return "/error/401";
    }

    @RequestMapping("/sys/logout")
    public String logout() {
        //1.获取当前用户
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return "redirect:/login";
    }
}

7.编写其他用户角色进入的界面:
StudentController:账号角色为admin的权限才可以访问

package com.hui.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/sys/admin")
public class StudentController {

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

    @RequestMapping("/update")
    public String updateStudent() {
        return "/student/update";
    }

    @RequestMapping("/delete")
    public String deleteStudent() {
        return "/student/delete";
    }

    @RequestMapping("/find")
    public String findStudent() {
        return "/student/find";
    }
}

UserController:账号权限为user的用户才可以访问

package com.hui.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/sys/user")
public class UserController {

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

    @RequestMapping("/update")
    public String updateStudent() {
        return "/user/update";
    }

    @RequestMapping("/delete")
    public String deleteStudent() {
        return "/user/delete";
    }

    @RequestMapping("/find")
    public String findStudent() {
        return "/user/find";
    }
}

8.template下的html界面:
在这里插入图片描述
login.html界面:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h3 th:text="${msg}"></h3>
登录界面
<form action="tologin" method="post">
    用户名:<input type="text" name="username"/></br>
    密码:<input type="text" name="password"/></br>
    <input type="checkbox" name="rememberMe">记住我?</br>
    <input type="submit" value="login"/></br>
</form>
</body>
</html>

9.数据库设计:
account表:账号表
在这里插入图片描述
accountPerm表:账号权限表
在这里插入图片描述
role表:角色表
在这里插入图片描述

总结:
1.shiro和thymeleaf整合时,thymeleaf版本必须为3.0及以上
2.在html中写th或者shiro标签有提示:

xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值