SpringBoot+shiro的demo

本文介绍如何在SpringBoot项目中整合Shiro进行权限认证。通过编写配置类和Realm,实现用户登录和角色权限控制。文章涵盖依赖引入、实体类设计、配置文件设置、表结构生成、Realm类实现及配置类编写,并提供测试代码和演示。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

SpringBoot整合shiro,需要写一个配置类和一个Realm即可,这里只讲实现功能,不讲原理。

1.引入依赖

		<dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>

2.实体类

user

import javax.persistence.*;

@Entity
@Table
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    @Column
    private String name;
    @Column
    private String password;
    @OneToOne
    @JoinColumn(name = "roleId")
    private Role role;
    @Column
    private String salt;

    public User() {
    }

    public User(String name, String password, Role role, String salt) {
        this.name = name;
        this.password = password;
        this.role = role;
        this.salt = salt;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Role getRole() {
        return role;
    }

    public void setRole(Role role) {
        this.role = role;
    }

    public String getSalt() {
        return salt;
    }

    public void setSalt(String salt) {
        this.salt = salt;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", password='" + password + '\'' +
                ", role=" + role +
                ", salt='" + salt + '\'' +
                '}';
    }

}

Role

@Entity
@Table
public class Role {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer roleId;
    @Column
    private String roleName;

    public Role() {
    }

    public Role(String roleName) {
        this.roleName = roleName;
    }

    public Integer getRoleId() {
        return roleId;
    }

    public void setRoleId(Integer roleId) {
        this.roleId = roleId;
    }

    public String getRoleName() {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }

    @Override
    public String toString() {
        return "Role{" +
                "roleId=" + roleId +
                ", roleName='" + roleName + '\'' +
                '}';
    }
}

3.配置文件

application.yml

#SpringBoot热部署
spring:
  devtools:
    restart:
      enabled: true
      additional-paths: src/main/java
      exclude: WEB-INF/**
  freemarker:
    cache: false
  datasource:
    username: root
    password: admin
    url: jdbc:mysql://localhost:3306/shiro?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect

4.生成表的结构

因为使用了JPA,表是自动生成的,下面是表的结构:

user
在这里插入图片描述
role
在这里插入图片描述
那么到这一步,准备工作已经完成了,在表设计中,我们设置一个用户有一个角色(OneToOne)

5.Realm类

package cn.yf.springboot_shiro_02.realm;

import cn.yf.springboot_shiro_02.bean.User;
import cn.yf.springboot_shiro_02.repository.UserRepository;
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.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

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


public class MyRealm extends AuthorizingRealm {

    @Autowired
    UserRepository userRepository;

    //权限认证
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String username = (String) SecurityUtils.getSubject().getPrincipal();
        User user = userRepository.findByName(username).get(0);
        String role = user.getRole().getRoleName();
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        Set<String> stringSet = new HashSet<>();
        stringSet.add(role);
        info.setRoles(stringSet);
        return info;
    }

    //登陆认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username = (String) token.getPrincipal();
        User user = userRepository.findByName(username).get(0);
        if(user != null){
            //交给shiro判断
            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username,user.getPassword(),getName());
            //设置盐
            simpleAuthenticationInfo.setCredentialsSalt(ByteSource.Util.bytes(user.getSalt()));
            return simpleAuthenticationInfo;
        }else{
            return null;
        }
    }

}

这个类继承于AuthorizingRealm 并且重写了它的两个方法,doGetAuthorizationInfo用于权限设置和doGetAuthenticationInfo用于登陆设置

6.配置类

package cn.yf.springboot_shiro_02.config;

import cn.yf.springboot_shiro_02.realm.MyRealm;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.apache.shiro.mgt.SecurityManager;
import org.springframework.context.annotation.DependsOn;

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

@Configuration
public class MyShiroConfig {

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //没有登录的用户请求登录时自动跳转到登录页面
        shiroFilterFactoryBean.setLoginUrl("/login");
        shiroFilterFactoryBean.setUnauthorizedUrl("/error");
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("/login", "anon");
        filterChainDefinitionMap.put("/register", "anon");
        filterChainDefinitionMap.put("/loginUser","anon");
        filterChainDefinitionMap.put("/error", "anon");
        filterChainDefinitionMap.put("/", "anon");
        filterChainDefinitionMap.put("/test/**", "authc");
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }


    @Bean
    public SecurityManager securityManager(MyRealm myRealm){
        DefaultWebSecurityManager defaultSecurityManager = new DefaultWebSecurityManager();
        defaultSecurityManager.setRealm(myRealm);
        return defaultSecurityManager;
    }

    @Bean
    public MyRealm myRealm(){
        MyRealm myRealm = new MyRealm();
        myRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        myRealm.setCachingEnabled(false);
        return myRealm;
    }

    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        // 散列算法:这里使用MD5算法;
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        // 散列的次数,比如散列两次,相当于 md5(md5(""));
        hashedCredentialsMatcher.setHashIterations(2);
        // storedCredentialsHexEncoded默认是true,此时用的是密码加密用的是Hex编码;false时用Base64编码
        hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
        return hashedCredentialsMatcher;
    }

    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
        return new LifecycleBeanPostProcessor();
    }

    @Bean
    @DependsOn({"lifecycleBeanPostProcessor"})
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

}

7.测试代码

package cn.yf.springboot_shiro_02.controller;

import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping("test")
@RestController
public class TestController {

    @RequiresRoles("admin")
    @RequestMapping("add")
    public String add(){
        return "添加";
    }

    @RequiresRoles("admin")
    @RequestMapping("delete")
    public String delete(){
        return "删除";
    }

    @RequiresRoles("admin")
    @RequestMapping("update")
    public String update(){
        return "更新";
    }

    @RequiresRoles(value={"user","admin"},logical= Logical.OR)
    @RequestMapping("query")
    public String query(){
        return "查询";
    }
    
}

8.演示

注册
在这里插入图片描述
账号:abc
密码:123456
在这里插入图片描述
在数据库中就可以看到加密后的密码,后面是它的盐
登陆
在这里插入图片描述
在这里插入图片描述
下面是关于权限的测试
根据上面的测试代码可以知道,当角色为admin时可以访问增删改查,而为user时只能访问查。
在这里插入图片描述
在这里插入图片描述
插入数据,我们可以知道,yf可以访问增删改查,而tht只能访问查
下面开始测试
登陆:yf
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述 在这里插入图片描述
在这里插入图片描述
登陆tht
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这样一个简单的权限认证和登陆验证,我们就完成了。我这里没有贴前端和一些跳转配置类的代码

9.总结

登陆注册加密的过程是这样的:首先当我们注册时,输入账号密码,首先生成盐,然后根据Shiro提供的Md5Hash类进行md5加密密码,然后把盐和密码都存入数据库中,当我们登陆时,从数据库取出盐和密码,把登陆时的密码和数据库中的盐进行md5加密,然后将结果和数据库中的密码进行匹配,如果结果一样就通过,不一样就抛出异常

参考博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值