Shiro安全框架(Shiro与SpringBoot整合开发)

Spring Boot与Shiro整合全面指南 本文还有配套的精品资源,点击获取 简介:本指南详细介绍了如何将Apache Shiro安全框架与Spring Boot相结合,实现完整的权限管理和用户认证授权功能。通过整合步骤的详细说明,包括依赖引入、配置、编码实现认证授权逻辑、安全管理器配置以及前端集成等,旨在帮助开发者快速构建安全且高效的应用程序。文章还涵盖了认证流程、授权流程、异常处理、会话管理以及数据库设计等关键点... 阅读详情

 

认证传送门:https://blog.youkuaiyun.com/JavaJieRui/article/details/108465791

授权传送门:https://blog.youkuaiyun.com/JavaJieRui/article/details/108495686

一、整合的思路

继上次说完了Shiro中的授权之后,Shiro中的认证和授权就全部为大家讲解完了,相信大家是跟着前两篇文章看过来的对Shiro在程序中进行认证和授权的操作都已经略知一二了,下面就为大家讲解一下Shiro在SpringBoot中的应用整合,同时在之前我们做的认证授权都是在代码中硬编码直接写死的,而在我们这次与SpringBoot的整合开发中,将会使用数据库中的数据来替代代码中的数据,更加真实的模拟我们日常在工作中对权限控制开发。

首先我们不论是开发任何功能,都是要对需求和思路进行清晰透彻的梳理和理解,虽然我们本次开发是模拟的真实开发场景,我们也要对开发之前的需求,实现的流程进行梳理。下面先为大家展示一张流程图,之后我会通过整张流程图再来讲解Shiro与SpringBoot整合开发的思路。

首先,我们要开发权限第一步是要先有一个SpringBoot的应用,这个我们做权限是对SpringBoot应用中的所有资源进行保护,如图所示,一个页面可以是一个被保护的资源。同样,程序中的一个方法也可以是一个被保护的资源,但是在一个SpringBoot的应用中,不仅仅要有被保护的资源,还要有公共资源,因为我们如果没有公共资源,登录或者注册的页面就没有办法被用户访问到,那假如说连注册和登录都是被保护起来的资源,那我们这个系统又怎么用呢?

所以门将所有的系统资源分为了两大类,一类是公共资源例如:登录页、登录的方法、注册页、注册的方法、可能还会有找回密码等等的这些统一划分为公共资源,而例如主页,或对系统内部数据的增删改查等的操作就被划分为受限资源,此时这些资源就要被我们用Shiro做的权限验证来保护起来。

前面我们说了那么多怎么把系统分为受限资源,公共资源,怎么用Shiro对他们进行权限的保护,但这些都有一个大的前提,必须让Shiro拿到SpringBoot应用中的所有的请求,为什么呢?应用中的所有资源都是通过客户通过一个又一个的请求来访问的,假如说我们的Shiro连请求都拿不到,又何谈划分资源何谈保护资源呢,所以所有的请求过来,首先要经过一个过滤器。对请求分类,看是受限资源还是公共资源,如果是公共资源不需要认证则直接放行,如果是受限资源呢?受限资源受限经过SecurityManager这个类进行认证处理,从我们的自定义Realms中拿到数据,同时Realms中还会有一系列的密码凭证匹配器,三列次数,算法名称等等一系列的操作。认证通过之后还要进行授权的操作,也就是说虽然你通过了认证,但是我们的Shiro还要来看看你是否有操作这个资源的权限,这样也就达到了相对细粒度的权限控制。

好了经过上面的一张流程图的展示以及对图的解释,我相信大家已经对流程有了相应的了解,下面就可以进行实战开发了。

二、环境搭建

首先上项目目录结构

 POM文件<这时还没有引入Shiro>

<?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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.csdn</groupId>
    <artifactId>springboot_shiro_jsp</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot_shiro_jsp</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </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>
        <!--jsp解析的依赖-->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
        </dependency>
        <!--可能会在jsp中用到c标签-->
        <dependency>
            <groupId>jstl</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

application.properties

#指定端口号
server.port=8090
#请求路径中加上项目名
server.servlet.context-path=/shiro
#指定项目名
spring.application.name=shiro

#将mvc视图改为jsp,前缀、后缀
spring.mvc.view.prefix=/
spring.mvc.view.suffix=.jsp

 在新建的webapp中新建一个index.jsp:

在IDEA中设置它的工作目录:

接下来启动,看一下效果:

好的,这样基本的springboot的一个应用就搭建起来了。

三、将Shiro与SpringBoot应用进行整合

Shiro与Springboot整合首先在项目目录中新建一个config包用来放项目中的各种配置类,之后编写一个类名叫ShiroConfig表明是Shiro的配置类,因为以后再项目中也许会放很多配置类,所以要见名知意表明是shiro的配置类。首先我们先将ShiroConfig的架子搭起来,具体代码如下:

/**
 * 用来整合shiro与springboot的配置类  关系就是一层依赖一层
 */
@Configuration
public class ShiroConfig {

    //创建ShiroFilter过滤器          负责拦截所有请求
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //给过滤器设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
        return shiroFilterFactoryBean;
    }

    //创建安全管理器
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(Realm realm){
        DefaultWebSecurityManager defaultWebSecurityManager = newDefaultWebSecurityManager(realm);
        //给安全管理器设置realm
        defaultWebSecurityManager.setRealm(realm);
        return defaultWebSecurityManager;
    }

    //创建自定义Realm
    @Bean
    public Realm realm(){
        MyRealm myRealm = new MyRealm();
        return myRealm;
    }
}

从上面的代码中可以看到最后一个为自定义Realm,那么我们也来新建一个名叫ralms的包。用来存放我们的自定义Realm:

/*
 * 自定义Realm
 */
public class MyRealm extends AuthorizingRealm {
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        return null;
    }
}

那么现在一个简单的架子我们就搭起来了,下面来完成具体实现。

那么虽然我们现在已经将架子搭建起来了,但是我们并没有在shiroconfig中写出来那些请求需要认证那些请求直接放行,所以所有的页面都能被访问到,现在就来看看具体实现。

四、具体实现

首先来看的是配置ShiroFilterFactoryBean配置受限资源和登录页路径:


    //创建ShiroFilter过滤器          负责拦截所有请求
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = shiroFilterFactoryBean(defaultWebSecurityManager);
        //声明一个map
        Map<String,String> map = new HashMap<>();
        //配置系统受限资源
        map.put("/index.jsp","authc"); //authc: 代表请求这个资源需要认证和授权
        //默认的登录页面路径
        shiroFilterFactoryBean.setLoginUrl("login.jsp"); //因为shiro与jsp的集成相对友好,所以这个路径如果不写也会默认去找,但也可以改变
        //将声明路径的map设置给过滤器
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);

        //给过滤器设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
        return shiroFilterFactoryBean;
    }

如上代码所示,如果配置了页面路径+autch,则视为此页面路径为受保护的资源页面,如果访问这个页面需要认证和授权,并且如果在同级目录下有一个login.jsp的页面会重定向到login.jsp并不需要配置,同时,如果你的login.jsp页面不在统计目录下或者叫其他名字,你也可以通过setLoginUrl的方式来设置你的登录页面来进行跳转。

那么authc这个在map中的value是什么呢?实际上这个authc是shiro为我们提供的一个过滤器的简称,其实还有很多简称下面为大家贴一张图:

虽然有很多过滤器但是最常用的也就是anon和authc这两种,如果日后想用其他的过滤器只需要根据自己的不同需求来选择不同功能的过滤器即可。

用户登录

接着我们再来编写一个简单的登录页面:

接着来编写一个controller:

@RequestMapping("/user")
@Controller
public class UserController {
    /**
     * 处理身份认证
     * @param username
     * @param password
     * @return
     */
    @RequestMapping("/login")
    public String login(String username,String password){
        //获取主体对象
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(username,password);
        try {
            subject.login(token);
            return "redirect:/index.jsp";
        } catch (IncorrectCredentialsException e) {
            e.printStackTrace();
            System.out.println("密码错误");
        } catch (UnknownAccountException e) {
            e.printStackTrace();
            System.out.println("用户名错误");
        }
        return "redirect:/login.jsp";
    }

此时我们编写完毕这个controller之后他一定会走我们的自定义Realm,但是此时我们的自定义Realm并没有写任何的具体实现所以不论我们怎么访问他都会重定向至登录页面:

那暂时我们写再将认证信息写死在代码中,测试一下:

/*
 * 自定义Realm
 */
public class MyRealm extends AuthorizingRealm {
    /**
     * 授权
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

    /**
     * 认证
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        String principal = (String) authenticationToken.getPrincipal();
        if (principal.equals("zhangsan")){
            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(principal,"123",this.getName());
            return simpleAuthenticationInfo;
        }
        return null;
    }
}

此时我将认证的信息固定为用户名:zhangsan 密码:123 再来测试一下:

效果:

进入了被保护的资源页面,系统主页。

那么此时还有一个问题,那以后我们所设计的系统一定不会只有这两个页面,一定会有很多被保护的页面,如果挨个去设置会跟麻烦,那么我们怎么办呢?这样的话就可以将一些比较少的公共资源设置为放行,将其他所有的页面设置为 /**,来将其保护起来。但一定要切记,公共资源的代码顺序一定是在被保护的资源的代码上方。

如下修改ShiroConfig:

/**
 * 用来整合shiro与springboot的配置类  关系就是一层依赖一层
 */
@Configuration
public class ShiroConfig {

    //创建ShiroFilter过滤器          负责拦截所有请求
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //声明一个map
        Map<String,String> map = new HashMap<>();
        //配置公共资源
        map.put("/login.jsp","anon"); //anon:代表这个资源为公共资源
        //配置系统受限资源
        map.put("/**","authc"); //authc: 代表请求这个资源需要认证和授权
        //将路径设置给过滤器
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        //默认的登录页面路径
        shiroFilterFactoryBean.setLoginUrl("login.jsp"); //因为shiro与jsp的集成相对友好,所以这个路径如果不写也会默认去找,但也可以改变


        //给过滤器设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
        return shiroFilterFactoryBean;
    }

    //创建安全管理器
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(Realm realm){
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager(realm);
        //给安全管理器设置realm
        defaultWebSecurityManager.setRealm(realm);
        return defaultWebSecurityManager;
    }

    //创建自定义Realm
    @Bean
    public Realm realm(){
        MyRealm myRealm = new MyRealm();
        return myRealm;
    }
}

效果我就不展示了,与上面一样。

用户登出

退出登录功能,首先将页面完善:

然后在UserController中编写退出的功能:


    /**
     * 用户登出功能
     * @return
     */
    @RequestMapping("/logout")
    public String logout(){
        Subject subject = SecurityUtils.getSubject();
        try {
            subject.logout();
            return "redirect:/login.jsp";
        } catch (Exception e) {
            e.printStackTrace();
            return "redirect:/index.jsp";
        }
    }

 shiro有一个好处就是如果你重启的话他也会在缓存中缓存你的登录信息,所以退出功能就是将之前的缓存信息清除掉,之后再访问资源页面时就退重定向到登录的页面。

那么本篇文章要讲的是shiro与springboot的整合开发模拟真实的开发场景,也就是说在真实的开发中我们一定不可能将用户的信息写死在代码中,一定是需要去数据库中获取用户的认证数据来实现认证,那么下面就为大家进一步的整合。

五、连接数据库进行认证处理

首先,连接数据库进行认证,我们需要有一个注册的页面,实现用户注册,同时我们要在用户注册的时候所填写的明文密码在保存数据库之前使用shiro提供的md5加密算法、并且加盐处理之后才能保存到数据库中。只有这样才能在之后进行认证的时候读出用户的加密密码。

用户注册(用户信息保存)

先来编写一个简单的注册页面:

此时用户注册时需要涉及到数据库的保存那么我们就来创建一个数据库,新建一张表来保存用户名以及密码。

id字段自增,然后分别有三个字段用户名、密码以及盐的信息,之前我们说过加在密码中的盐一定要保存到数据库中去,只有这样才能保证我们在认证的时候加入相同的盐保证用户认证信息的通过。

既然我们需要用到随机盐那么我们就需要用到一个盐工具类那么我们在工程下的utils包中先来编写一个盐工具类:


/**
 * 随机盐工具类
 */
public class SaltUtils {

    public String getSalt(int n) {
        //首先定义一个char数组
        char[] chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890!@#$%^&*()".toCharArray();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < n; i++) {
            //每次拿到一个随机字符
            char aChar = chars[new Random().nextInt(chars.length)];
            //拼接随机字符
            sb.append(aChar);
        }
        return sb.toString();
    }

    /**
     * 测试随机盐工具类
     * @param args
     */
    public static void main(String[] args) {
        SaltUtils saltUtils = new SaltUtils();
        String salt = saltUtils.getSalt(8);
        System.out.println(salt);
    }
}

修改之后的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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.csdn</groupId>
    <artifactId>springboot_shiro_jsp</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot_shiro_jsp</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </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>
        <!--jsp解析的依赖-->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
        </dependency>
        <!--可能会在jsp中用到c标签-->
        <dependency>
            <groupId>jstl</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
        <!--shiro与SpringBoot整合的依赖-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring-boot-starter</artifactId>
            <version>1.6.0</version>
        </dependency>
        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
        </dependency>
        <!--mybatis相关的依赖-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.3</version>
        </dependency>
        <!--druid数据库连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.1</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.3.4.RELEASE</version>
            </plugin>
        </plugins>
    </build>

</project>

新建一个实体类:

@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {
    private String id;
    private String username;
    private String password;
    private String salt;
}

接着对项目的application.properties文件进行修改:

#指定端口号
server.port=8090
#请求路径中加上项目名
server.servlet.context-path=/shiro
#指定项目名
spring.application.name=shiro

#将mvc视图改为jsp,前缀、后缀
spring.mvc.view.prefix=/
spring.mvc.view.suffix=.jsp

#数据源相关配置
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#数据源类型
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.url=jdbc:mysql://localhost:3306/shiro?characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=root
mybatis.mapper-locations=classpath:com/csdn/mappers/*.xml

相关配置写完后就可以来编写注册的方法并将用户的数据保存在数据库中。

dao层代码:

@Mapper
public interface UserDao {
    /**
     * 用户注册,保存
     * @param user
     */
    void save(User user);
}

service层代码:

public interface UserService {
    /**
     * 用户注册保存信息
     * @param user
     */
    void save(User user);
}

serviceImpl层代码:

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    /**
     * 用户注册保存用户信息
     * @param user
     */
    @Override
    public void save(User user) {
        //获取随机盐
        String salt = new SaltUtils().getSalt(8);
        //拿到用户输入的密码
        String password = user.getPassword();
        //新建一个md5Hash的对象传参
        Md5Hash md5Hash = new Md5Hash(password,salt,1024);
        //s 即为处理后的密码
        String s = md5Hash.toHex();
        //保存
        user.setPassword(s);
        user.setSalt(salt);
        userDao.save(user);
    }
}

上面业务层的代码主要是对密码的处理,一方面是获取随机盐,然后保存随机盐,另一方面则是将输入的密码获取到之后进行加密处理后再将其密码进行保存。

controller层代码:

 /**
     * 用户注册
     * @param user
     * @return
     */
    @RequestMapping("/register")
    public String Register(User user){
        try {
            userService.save(user);
            return "redirect:/login.jsp";
        } catch (Exception e) {
            e.printStackTrace();
            return "redirect:/register.jsp";
        }
    }
}

保存成功后直接重定向到登录页面,如果报错之后则在原页面。

mapper.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.csdn.springboot_shiro_jsp.moudles.user.login.dao.UserDao">
    <insert id="save" useGeneratedKeys="true" keyProperty="id" parameterType="com.csdn.springboot_shiro_jsp.moudles.user.login.pojo.User">
        insert into t_user values(#{id},#{username},#{password},#{salt})
    </insert>
</mapper>

运行过程就不展示了,直接到数据库去查看一下结果,我输入的是:用户名:zhangsan 密码:123456,数据库截图:

用户信息认证

现在我们已经将注册之后的用户信息用md5的方式加密并加盐散列1024次之后将密码以及随机盐的内容存入了数据库中,现在只需要将用户信息读取之后以相同的方式解密认证即可。

首先我们先编写dao层因为需要一个根据用户输入的用户名来获取用户信息的方法,所以首先编写一个dao层的代码以及mapper文件:

@Mapper
public interface UserDao {
    /**
     * 用户注册,保存
     * @param user
     */
    void save(User user);

    /**
     * 根据用户名查询用户
     * @param username
     * @return
     */
    User findByUsername(String username);
}

mapper文件修改:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.csdn.springboot_shiro_jsp.moudles.user.login.dao.UserDao">
    <!--保存用户信息-->
    <insert id="save" useGeneratedKeys="true" keyProperty="id" parameterType="com.csdn.springboot_shiro_jsp.moudles.user.login.pojo.User">
        insert into t_user values(#{id},#{username},#{password},#{salt})
    </insert>
    <!--根据用户名查询用户-->
    <select id="findByUsername" parameterType="string" resultType="com.csdn.springboot_shiro_jsp.moudles.user.login.pojo.User">
        select id,username,password,salt from t_user
        where username=#{username}
    </select>
</mapper>

业务层代码:

public interface UserService {
    /**
     * 用户注册保存信息
     * @param user
     */
    void save(User user);

    /**
     * 根据用户名查询用户
     * @param username
     * @return
     */
    User findByUsername(String username);
}

业务层实现类

@Service("userService")
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    /**
     * 用户注册保存用户信息
     * @param user
     */
    @Override
    public void save(User user) {
        //获取随机盐
        String salt = new SaltUtils().getSalt(8);
        //拿到用户输入的明文密码
        String password = user.getPassword();
        //新建一个md5Hash的对象传参
        Md5Hash md5Hash = new Md5Hash(password,salt,1024);
        //s 即为处理后的密码
        String s = md5Hash.toHex();
        //保存
        user.setPassword(s);
        user.setSalt(salt);
        userDao.save(user);
    }

    /**
     * 根据用户名查询用户
     * @param username
     * @return
     */
    @Override
    public User findByUsername(String username) {
        User user = userDao.findByUsername(username);
        return user;
    }
}

根据之前我们的已经了解过得shiro的匹配规则此时,我们还需要去自定义Realm中去修改,从数据库中获取用户信息,所以我们这时要去自定义Realm去修改。

而此时因为我们的用户信息是通过数据库获取的,自定义的Realm又不在工厂中被Spring管理所以此时我们需要一个工具类即为通过类名来获取工厂中的对象。


/**
 * 根据bean名字获取工厂中指定的bean对象
 */
@Component
public class ApplicationContextUtils implements ApplicationContextAware {
    private static ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.context=applicationContext;
    }

    public static Object getBean(String beanName){
        Object bean = context.getBean(beanName);
        return bean;
    }
}

之前我们通过@Service标签中声明了一个名字为userService,这样就可以通过对象名字获取工厂中的对象。

之后修改自定义Realm:

/*
 * 自定义Realm
 */
public class MyRealm extends AuthorizingRealm {
    /**
     * 授权
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

    /**
     * 认证
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        String principal = (String) authenticationToken.getPrincipal();
        //在工厂中获取service对象
        UserService userService = (UserService) ApplicationContextUtils.getBean("userService");
        User user = userService.findByUsername(principal);
        //判断user不为空时处理
        if (!ObjectUtils.isEmpty(user)){
            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(),
                                                                ByteSource.Util.bytes(user.getSalt()),this.getName());
            return simpleAuthenticationInfo;
        }
        return null;
    }
}

因为我们将密码加密的策略已经修改为了md5加散列所以要将密码匹配策略修改,修改shiroConfig:

/**
 * 用来整合shiro与springboot的配置类  关系就是一层依赖一层
 */
@Configuration
public class ShiroConfig {

    //创建ShiroFilter过滤器          负责拦截所有请求
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //声明一个map
        Map<String,String> map = new HashMap<>();
        //配置公共资源
        map.put("/user/**","anon"); //anon:代表这个资源为公共资源
        map.put("/register.jsp","anon");
        //配置系统受限资源
        map.put("/**","authc"); //authc: 代表请求这个资源需要认证和授权
        //将路径设置给过滤器
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        //默认的登录页面路径
        shiroFilterFactoryBean.setLoginUrl("/login.jsp"); //因为shiro与jsp的集成相对友好,所以这个路径如果不写也会默认去找,但也可以改变


        //给过滤器设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
        return shiroFilterFactoryBean;
    }

    //创建安全管理器
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(Realm realm){
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager(realm);
        //给安全管理器设置realm
        defaultWebSecurityManager.setRealm(realm);
        return defaultWebSecurityManager;
    }

    //创建自定义Realm
    @Bean
    public Realm realm(){
        MyRealm myRealm = new MyRealm();
        //修改密码凭证匹配器  修改匹配策略
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        //密码加密方式
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        //设置散列次数
        hashedCredentialsMatcher.setHashIterations(1024);
        myRealm.setCredentialsMatcher(hashedCredentialsMatcher);
        return myRealm;
    }
}

全部修改完成之后,启动项目再来访问一下页面:

成功!

那么springboot整合shiro连接数据库的简单实现就写到这里,后续的文章会继续加上从数据库获取授权信息的简单实现,希望大家共同学习一起成长。

UOS忘记密码别慌!用LiveCD工具5分钟搞定,附命令行救援模式详细步骤 本文详细介绍了UOS系统密码遗忘时的两种解决方案:图形化LiveCD工具和命令行救援模式。针对普通用户,LiveCD工具提供5分钟快速重置密码的便捷操作;而技术人员可通过命令行深度解析系统分区挂载与密码修改流程。文章还包含安全加固建议,帮助用户预防类似问题发生。 阅读详情

相关推荐

FPGA新手必看:XC7Z020最小系统搭建全攻略(附原理图解析)

本文为FPGA新手提供了XC7Z020最小系统搭建的完整实战指南。文章详细解析了电源架构、时钟复位、配置调试等关键硬件电路设计,并重点剖析了外围电路与端口引脚的连接要点及常见陷阱,帮助读者从零开始构建稳定可靠的开发平台。

weixin_29189377的博客 359

server.servlet.contextPath 和 spring.mvc.servlet.path 对比

另一方面,servlet路径表示主DispatcherServlet的路径。默认值类似于上下文路径,即(“/”),可以通过配置不同的spring.mvc.servlet.path属性进行更改。假设servlet属于servlet上下文,更改上下文路径也会影响servlet路径。它是应用程序的根,默认情况下,Spring Boot提供根上下文路径(“/”)上的内容。然后应用程序servlet路径将变为http://localhost:8080/context-path/servlet-path。

qq_46050989的博客 4602

DBeaver连接IoTDB避坑指南:手动配置JDBC驱动的完整流程(附问题排查)

本文提供了在DBeaver中手动配置Apache IoTDB JDBC驱动的完整流程与避坑指南。详细解析了从驱动创建、库文件添加(关键是指向整个lib目录)到连接测试的每一步,并重点针对常见的ClassNotFoundException、连接拒绝等错误提供了深度排查方案,帮助用户高效建立图形化管理连接。

weixin_28327051的博客 394

shiro配置不被拦截路径,不生效踩得坑记录

请求的requestURI的路径是 /gaw-job/test/test3 所以我很自然的就配置shiroConfig的不拦截路径为 然后一直不生效,后来断点到shiro路径匹配的时候 shiro匹配的路径request.getServletPath() 而我们上方的request.getRequestURI()(就是/gaw-job/test/test3)等于 request.getContextPath() + request.getServletPath() request.getContextPa

qq_48721706的博客 5906

Python+pytest+requests 自动化测试框架

环境准备 本次选用的是Python+Pytest+requests来搭建自动化框架,需要有 Python 环境(3.x 版本),安装pytest和requests,不会安装的自行去网上搜教程。 关于 Pytest 的基本使用,这里不作讲解,不清楚的可以看我之前的文章。 简单 Demo 我们先创建一个项目,取名api-test,然后在下面创建一个test_api.py的文件 # test_api.py import pytest import requests def test_01():

weixin_42686892的博客 1万+

达芬奇Pro开发板硬件验证全流程指南

FPGA开发板硬件验证是嵌入式系统可靠性的基石,其核心在于理解SoC启动时序、IO资源映射与高速接口物理层约束。基于Xilinx Zynq-7000异构架构的开发平台,需围绕电源完整性、时钟稳定性、信号完整性及软硬协同启动链路展开分层测试。技术价值体现在规避量产失效率、支撑高可靠工业控制与实时图像处理等场景。典型应用涵盖千兆以太网通信验证、DVP摄像头与RGB LCD显示链路调试、以及SFP+光纤接口PRBS误码率测试。本文聚焦达芬奇Pro系列开发板,深入解析JTAG下载、QSPI Flash重烧录、RGM

weixin_42453228的博客 122

spring boot中shiro使用自定义注解屏蔽接口鉴权

spring boot整合shrio后,所有接口都需要在鉴权通过后才能使用。某些接口(比如登录接口)是不需要鉴权的,就需要手动配置一下。本文介绍了一种通过自定义注解,实现屏蔽接口鉴权的功能的方法,这样在需要屏蔽接口鉴权的时候,就不用反复修改shiro配置了。...

LLittleF的博客 2936

RK3588-NANOPC-T6移植ubuntu+xenomai内核

更具网址下载相应的内核。导入到开发板并执行内核。

qq_42007446的博客 572

最新版Shiro-SpringBoot项目实战笔记

shiro的配置文件是提供权限数据的,以.ini结尾。 shiro.int:

有空就记录一下 1522

保姆级教程:用dnSpy调试Unity游戏,从找DLL到打断点全流程(附调试版mono.dll下载)

本文详细介绍了使用dnSpy调试Unity游戏的全流程,包括环境准备、关键程序集定位、mono.dll版本匹配与替换、调试环境配置及实战技巧。通过本教程,开发者可以高效进行Unity游戏逆向分析,适用于学习设计模式或安全审计等场景。

weixin_30236595的博客 577

Unity工业数据可视化:基于ModbusTCP的实时数据通信实战

本文详细介绍了在Unity中实现工业数据可视化的核心方法,重点讲解了基于ModbusTCP协议与PLC等工业设备进行实时数据通信的实战流程。内容涵盖NModbus库的集成、Modbus Slave模拟器的使用、TCP连接的建立与多线程安全通信、数据读写与解析,以及如何将实时数据驱动3D场景,为构建工业数字孪生与监控系统提供完整解决方案。

ol7890123的博客 553

shiro静态资源放行,亲测有效!

spring boot + shiro静态资源放行 Shiro配置类配置 按照常规流程,在ShiroConfigration(配置类,自定义类名)中放行静态资源 下面展示一些 内联代码片。 // An highlighted block Map<String,String> filterMap = new LinkedHashMap<>(); //根目录不拦截 // filterMap.put("/","anon"); //登录页面不拦截

weixin_42768469的博客 6071

keil软件调试(Debug)仿真教程(软件调试和硬件调试的区别)及常用调试按键详解

单片机的调试分为两种,一种是使用软件模拟调试,第二种是硬件调试,两种调试方式各有不同,软件模拟调试有误差,而硬件调试,借用仿真器调试是嵌入式开发中常用方法。

weixin_51624736的博客 3万+

Shiro安全框架与SpringBoot整合以及Thymeleaf模板的整合开发补充

shiro使用jsp展示用户名补充 前面基本上已经将所有的shiro框架的急促使用,实战开发的所有内容都编写完毕了,接下来就是一些关于shiro框架的一些其他补充,比如:我们要将当前登录的用户放到首页中展示其用户名,该怎么做呢?在我们的jsp页面中,只需要shiro提供的标签就可以做到: 这个标签,就是获取当前登录人的身份信息: 可以看到当前登录人的用户名展示在了首页。 shiro与Thymeleaf整合开发 ...

JavaJieRui的博客 272

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

Shiro安全框架(Shiro与SpringBoot整合开发)授权部分(一)编码

对于shiro框架我们之前已经分别叙述了shiro在.ini文件中的认证,shiro代码中硬编码的认证实现,以及我们将shiro与springboot整合的认证实现,还有我们将shiro与springboot整合之后,连接数据库的认证实现,接下来我们继续为大家展开来研究一下shiro与springboot整合之后连接数据库的授权方面的实现,其实与之前shiro连接数据库做认证的实现思路相似,我们也首先来理一下思路。 一、关于shiro与springboot整合的授权实现思路 首先,shiro这个安全框架

JavaJieRui的博客 408

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

SpringBoot 2.0 开发案例之整合Shiro安全框架

点击▲关注 “爪哇笔记” 给公众号标星置顶更多精彩 第一时间直达一、前言为了管理撸主的小黄图,特别安排了一个后台管理系统,权限管理当然是首选Shiro了。二、表设计至少需要以下几...

不是太高的手的专栏 257

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

Shiro安全框架(Shiro与SpringBoot整合开发)授权部分(三)权限实现

之前我们再shiro中已经实现了认证数据的数据库获取,权限数据的数据库获取,最后我们再来看一下有关权限字符串集合的获取,其实权限字符串的获取和角色的获取集合也是大同小异的,下面就可以来看一下这个权限的细粒度的权限控制。 首先我们来分析一下,在数据库中的表:分别是 t_role,t_perms,t_role_perms这三张表,t_role是角色表,t_perms为权限表,t_roler_perms为权限与角色的中间表,那么我们最终的目的就是要通过角色id来获取权限字符串,url,角色名称。 那么想好需要

JavaJieRui的博客 227

Shiro安全框架(Shiro与SpringBoot整合开发)缓存(五)Shiro缓存实现(redis缓存)

这篇文章我们接上回的shiro自带缓存ehcahe cacheManager的实现,但是暴露了一个小问题是程序一结束缓存就会消失,所以此时我们就用redis做缓存,解决这个问题。 首先我们将redis的坐标引进来到POM文件中: <!--redis整合springboot--> <dependency> <groupId>org.springframework.boot</groupId>

JavaJieRui的博客 407

Shiro安全框架(Shiro与SpringBoot整合开发)CacheManager(四)Shiro缓存实现(ehCache自带缓存)

在之前的文章中,介绍shiro一直从shiro的.ini文件的权限认证,到在自定义Realm中的权限认证的实现,到后期我们链接数据库的权限实现,然后是自定义Realm中授权信息实现,到链接数据库的授权实现。粗粒度的授权实现角色,以及细粒度的权限字符串的授权实现。我们都一一做了解释和Demo。那么我们现在要考虑到一个系统的雏形搭建起来之后,就要考虑到一些优化的小问题,逼如说我们的权限数据一般来说不会有太大的变动,然而我们这时做的权限,授权等等的操作一直都会访问数据库,如果我们的系统访问量很大,相应的访问首页的

JavaJieRui的博客 630

springboot Shiro 单点登录使用配置教程

spring boot整合Shiro实现单点登录

m0_67393593的博客 1274

Shiro安全框架(Shiro与SpringBoot整合开发)授权部分(二)连接数据库

我们之前已经为大家展示了一下shiro授权数据在Realm中的简单实现,那这时我们就要将shiro的授权数据持久化至数据库中去,因为我们将来一个系统一定是有很多用户,并且也是拥有很多角色的,我们的授权数据不可能总是在Realm中写,所以这时,我们就要将授权数据持久化进数据库中去。接下来就简单分析一下思路。 ...

JavaJieRui的博客 361

javaee互联网轻量级框架整合开发_SpringBoot 2.0 开发案例之整合Shiro安全框架

点击▲关注 “爪哇笔记” 给公众号标星置顶更多精彩 第一时间直达一、前言为了管理撸主的小黄图,特别安排了一个后台管理系统,权限管理当然是首选Shiro了。二、表设计至少需要以下几张表,具体如何设计要视自己的业务而定:sys_user:用户表sys_role:角色表sys_menu:菜单表sys_user_role:用户对应角色sys_role_menu:角色对应菜单三、整合pom...

weixin_39719101的博客 175

Shiro安全框架(Shiro与SpringBoot整合开发)之图片验证码(五)

这篇博文主要讲解实现图片验证码的实现,之前已经将权限,认证,自带缓存,redis缓存,都实现了,现在我们就来了解一下简单的图片验证码的实现: 首先,我们要来修改一下登录的页面,login.jsp这个页面: <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>后台管理系统</title> </head>

JavaJieRui的博客 405

3万字《SpringBoot微服务开发——Shiro(安全)》_springboot shiro 微服务

​ Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。Shiro可以非常容易的开发出足够好的应用,不仅可以用在JavaSE环境,也可以用在JavaEE环境。下载地址: http://shiro.apache.org/

2401_84615622的博客 1234

springboot+shiro+mybatis maven项目整合

springboot+shiro+mybatis安全框架整合 springboot+shiro+mybatis maven项目整合 1.引入依赖 <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.10</version> </dependency>

weixin_45815347的博客 536

SpringBoot + Shiro安全框架 + vue(保姆级从零开始)

Component@Autowired// 自定义授权方法@Override// 自定义登录认证方法@Override// 1.获取用户身份信息// 2. 调用业务层获取用户信息(数据库中)// 判断并将数据完成封装if (!

cygqtt的博客 647
上一篇: Shiro安全框架(授权部分)
下一篇: 导出easyExcel的简单使用
JavaJieRui
博客等级 码龄7年 10粉丝 19原创
评论 16
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值