狂神说Java之Springboot整合Shiro

Springboot整合Shiro

文章是观看狂神说Java的Springboot整合Shiro的视频时写的随笔,大家可以参考下编写代码的过程,里面也有我写的一点注意事项。
完整代码我已经放在gitee上了,需要的可以自取。也可以参考我后面参考引用的博主的博客,里面也有完整的代码。
加油!打工人!

一、实验过程

目录结构

service层再建个impl包会更加层次分明
image-20201216154906380

1、实验准备

1.1 导入依赖

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

1.2 前端页面

index.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h>首页</h>
    <!--/*@thymesVar id="msg" type=""*/-->
    <div th:text="${msg}"></div>
    <a th:href="@{/user/add}">add</a>
    <a th:href="@{/user/update}">update</a>


</body>
</html>

login

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>登录</h1>
    <hr>

    <form th:action="@{/login}">
        用户名:<input type="text" name="username"><br>
        密码:<input type="password" name="password">
        <br>
        <input type="submit" name="提交">
    </form>
</body>
</html>

add

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

update

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

1.3 数据库准备

create table user
(
    id    int(20) auto_increment primary key,
    name  varchar(30) null,
    pwd   varchar(30) null,
    perms varchar(50) null
)charset = utf8;

2、测试01

设置访问权限,如果没权限则进入登陆界面

2.1 编写Shiro配置类

思路:ShiroFilterFactoryBean会拦截前端请求交给DefaultWebSecurityManager,再交给userRealm进行认证和授权处理
主要编写userRealm、DefaultWebSecurityManager、ShiroFilterFactoryBean三个bean对象

注意:我们可以从下往上写,逐步添加

@Configuration
public class ShiroConfig {

    //ShiroFilterFactoryBean (第三步:连接到前端)
    @Bean
    public ShiroFilterFactoryBean getShiroFilterBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        //设置安全管理器
        bean.setSecurityManager(defaultWebSecurityManager);

        return bean;
    }

    //DefaultWebSecurityManager (第二步:管理realm对象)
    @Bean(name="securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //关联UserRealm
        securityManager.setRealm(userRealm);
        return securityManager;

    }

    //创建realm对象,需要自定义类 (第一步:创建realm对象)
    @Bean(name="userRealm")  //@Bean注解后便被spring托管,不加name属性,默认name值为方法名,这里就加一下吧
    public UserRealm userRealm(){
        return new UserRealm();
    }

}

在ShiroFilterFactoryBean方法内添加过滤器,设置访问权限,如果没权限则进入登陆界面

//添加shiro的内置过滤器
/*
anon: 无需认证即可访问
authc: 必须认证才能用
user: 必须拥有 “记住我” 功能才能用
perms: 拥有对某个资源的权限才能用
role: 拥有某个角色权限才能访问
*/

Map<String,String> filterMap = new LinkedHashMap<>();
//拦截
filterMap.put("/user/add","anon");
filterMap.put("/user/update","authc");
//也可使用通配符*
//filterMap.put("/user/*","authc");

bean.setFilterChainDefinitionMap(filterMap);
//若访问时用户未认证,则跳转至登录页面
bean.setLoginUrl("/toLogin");

2.2 编写UserRealm类

只要进行登陆操作(subject.login)就会执行doGetAuthenticationInfo方法

//自定义UserRealm extends AuthorizingRealm
public class UserRealm extends AuthorizingRealm {
	//授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行了授权");
        return null;
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("执行了认证");
        return null;
    }
}

2.3 编写HellloController进行测试结果

@Controller
public class HellloController {

    @RequestMapping({"/", "/index"})
    public String toIndex(Model model){
        model.addAttribute("msg", "hello");
        return "index";
    }

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

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

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

3、登陆认证

整体流程:当我们在登陆页面输入账号密码,会执行HelloController的login登陆操作,在使用subject.login(token)时会执行shiro里的UserRealm里的认证方法doGetAuthenticationInfo。

在HelloController中添加登陆认证方法,都是根据入门案例来写的

@RequestMapping("/login")
    public String login(String username,String password,Model model){
        //获取当前用户
        Subject subject = SecurityUtils.getSubject();
        //封装用户的登录数据
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);

        try{
            subject.login(token); //执行登录的方法,如果没有异常就说明ok了
            return "index";
        }catch (UnknownAccountException e){ //用户名不存在
            model.addAttribute("msg","用户名不存在!");
            return "login";
        }catch (IncorrectCredentialsException e){
            model.addAttribute("msg","密码错误!");
            return "login";
        }
    }

当我们执行 subject.login登陆操作时,Shiro会执行UserRealm里的认证方法doGetAuthenticationInfo

@Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("执行了认证");

        //用户名、密码  模拟从数据库中获取
        String name = "root";
        String password = "1111";

        UsernamePasswordToken userToken = (UsernamePasswordToken) token;


        if (!userToken.getUsername().equals(name)){
            return null; //抛出异常 UnknownAccountException
        }
        //密码认证,shiro做~
        return new SimpleAuthenticationInfo("",password,"");

    }

4、整合Mybatis

目标:使的用户登陆认证数据从数据库中取出

4.1 导入依赖

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.18</version>
</dependency>
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.21</version>
</dependency>

4.2 配置application.yml

这里配置mybatis扫描的包和别名

spring:
  datasource:
    username : root
    password: qrj15521026074
    url : jdbc:mysql://localhost:3306/mybatis01?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource # 自定义数据源

    #Spring Boot 默认是不注入这些属性值的,需要自己绑定
    #druid 数据源专有配置
    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,stat:监控统计、log4j:日志记录、wall:防御sql注入
    #如果允许时报错  java.lang.ClassNotFoundException: org.apache.log4j.Priority
    #则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
    filters: stat,wall,log4j
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

mybatis:
  type-aliases-package: com.qiu.pojo
  mapper-locations: classpath:mapper/*.xml

4.3配置UserMapper.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.qiu.mapper.UserMapper">

    <select id="queryUserByName" parameterType="String" resultType="com.qiu.pojo.User">
       select * from user where name = #{name};
    </select>

</mapper>

4.4 编写主要类和接口

编写User类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Integer id;
    private String name;
    private String pwd;
    private String perms;
}
编写UserMapper接口
@Repository
@Mapper
public interface UserMapper {
    public User queryUserByName(String name);
}
编写UserService接口
public interface UserService {
    public User queryUserByName(String name);
}
编写UserServiceImpl实现类
@Service
public class UserServiceImpl implements UserService{

    @Autowired
    UserMapper userMapper;

    @Override
    public User queryUserByName(String name) {
        return userMapper.queryUserByName(name);
    }

}

4.5 重新编写UserRealm类

让用户数据连接数据库

 @Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("执行了认证");

      /*
        //用户名、密码  模拟从数据库中获取
        String name = "root";
        String password = "1111";
        if (!userToken.getUsername().equals(name)){
            return null; //抛出异常 UnknownAccountException
        }
        //密码认证,shiro做~
        return new SimpleAuthenticationInfo("",password,"");
        */
        
        UsernamePasswordToken userToken = (UsernamePasswordToken) token;
        //连接数据库
        User user = userService.queryUserByName(userToken.getUsername());
        if (user == null){
            return null; //抛出异常 UnknownAccountException
        }
        //密码认证,shiro做~
        return new SimpleAuthenticationInfo("",user.getPwd(),"");

    }

5、请求授权

目标:通过从数据库中获取用户权限资源,访问对应权限的方法

5.1 重写ShiroConfig

user类增加一个perms权限访问

在ShiroConfig中设置拦截登陆

 @Bean
    public ShiroFilterFactoryBean getShiroFilterBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        //设置安全管理器
        bean.setSecurityManager(defaultWebSecurityManager);

        //添加shiro的内置过滤器
        /*
        anon: 无需认证即可访问
        authc: 必须认证才能用
        user: 必须拥有 “记住我” 功能才能用
        perms: 拥有对某个资源的权限才能用
        role: 拥有某个角色权限才能访问
        */

        Map<String,String> filterMap = new LinkedHashMap<>();
        //登陆认证拦截:未登录会跳转登陆页面
        filterMap.put("/user/add","anon");
        filterMap.put("/user/update","authc");
        //也可使用通配符*
        //filterMap.put("/user/*","authc");

        //登陆后授权,正常情况下没有授权会跳转到未授权页面
        filterMap.put("/user/add","perms[user:add]");
        filterMap.put("/user/update","perms[user:update]");

        bean.setFilterChainDefinitionMap(filterMap);

        //若访问时用户未认证,则跳转至登录页面
        bean.setLoginUrl("/toLogin");

        //若访问时用户未被授权,则跳转至未授权页面
        bean.setUnauthorizedUrl("/noauth");

        return bean;
    }

5.2 重写UserRealm

添加用户的权限,从认证中获取用户信息,取出权限perms

//授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行了授权");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //授权操作
        //info.addStringPermission("user:add");

        //拿到当前登录的对象
        Subject subject = SecurityUtils.getSubject();
        User currentUser = (User) subject.getPrincipal(); //拿到user对象
        System.out.println(currentUser.getPerms());
        info.addStringPermission(currentUser.getPerms());

        return info;
    }

这里要在认证doGetAuthenticationInfo方法里return时传入user资源,使之能在授权方法中使用获取权限资源

 @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("执行了认证");
		//...................
        //这里在登陆后传入user资源
        return new SimpleAuthenticationInfo(user,user.getPwd(),"");

    }

5.3 在HellloController添加未授权操作

@RequestMapping("/noauth")
    @ResponseBody
    public String unauthorized(){
        return "未授权无法访问此页面";
    }

6、整合thymeleaf

目标:实现特点用户访问特定页面,权限不同,访问到的功能不同

6.1 导入依赖

 <!--导入thymeleaf依赖-->
 <dependency>
     <groupId>org.thymeleaf</groupId>
     <artifactId>thymeleaf-spring5</artifactId>
     <version>3.0.11.RELEASE</version>
 </dependency>
 <dependency>
     <groupId>org.thymeleaf.extras</groupId>
     <artifactId>thymeleaf-extras-java8time</artifactId>
     <version>3.0.4.RELEASE</version>
 </dependency>

<!--shiro-thymeleaf整合-->
<dependency>
    <groupId>com.github.theborakompanioni</groupId>
    <artifactId>thymeleaf-extras-shiro</artifactId>
    <version>2.0.0</version>
</dependency>

6.2 在ShiroConfig添加整合thymeleaf

 //整合ShiroDialect:用来整合shiro thymeleaf
    @Bean
    public ShiroDialect getShiroDialect(){
        return new ShiroDialect();
    }

6.3 使用thymeleaf重写index登陆页面

导入shiro和thymeleaf的约束

通过shiro:hasPermission指定权限

<!DOCTYPE html>
<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>
</head>
<body>
<h1>首页</h1>
    <div th:if="${session.loginUser==null}">
        <a th:href="@{/toLogin}">登录</a>
    </div>
    <p th:text="${msg}"></p>
    <hr>
    <div shiro:hasPermission="user:add">
        <a th:href="@{/user/add}">add</a>
	</div>
    <div shiro:hasPermission="user:update">
        <a th:href="@{/user/update}">update</a>
    </div>

    <a th:href="@{/logout}">注销</a>
</body>
</html>

7、结果

数据库权限

image-20201214220801215

root账号登陆界面

image-20201214220706153

狂神账号登陆界面

image-20201214220825264

二、实验问题

1、插件报错

打包遇到错误Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.22.2:test

解决办法:修改pom.xml文件

<plugins>
	<plugin>
		<groupId>org.apache.maven.plugins</groupId>
		<artifactId>maven-surefire-plugin</artifactId>
		<version>2.19.1</version>
	</plugin>
</plugins>

2、IDEA2020.1启动SpringBoot项目出现java程序包:xxx不存在

setting->bulid,execution,Deployment->bulid tools->maven->runner

勾选上Delegate ide bulid/run actions to Maven

参考:IDEA2020.1启动SpringBoot项目出现java程序包:xxx不存在

参考

【狂神说Java】SpringBoot整合Shiro框架

SpringBoot整合Shiro

)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
整理自尚硅谷视频教程springboot高级篇,并增加部分springboot2.x的内容 一、Spring Boot与缓存 一、JSR107 Java Caching定义了5个核心接口,分别是CachingProvider, CacheManager, Cache, Entry 和 Expiry。 • CachingProvider定义了创建、配置、获取、管理和控制多个CacheManager。一个应用可 以在运行 期访问多个CachingProvider。 • CacheManager定义了创建、配置、获取、管理和控制多个唯一命名 的Cache,这些Cache 存在于CacheManager的上下文中。一个CacheManager仅被一个 CachingProvider所拥有。 • Cache是一个类似Map的数据结构并临时存储以Key为索引的值。一个 Cache仅被一个 CacheManager所拥有。 • Entry是一个存储在Cache中的key-value对。 • Expiry 每一 个存储在Cache中的条目有一个定义的有效期。一旦超过这个时间,条目为过期 的状态。一旦过期,条 目将不可访问、更新和删除。缓存有效期可以通过ExpiryPolicy设置。 二、Spring缓存抽象 Spring从3.1开始定义了org.springframework.cache.Cache 和 org.springframework.cache.CacheManager接口来统一不同的缓存技术; 并支持使用JCache(JSR- 107)注解简化我们开发; • Cache接口为缓存的组件规范定义,包含缓存的各种操作集合; • Cache接 口下Spring提供了各种xxxCache的实现;如RedisCache,EhCacheCache , ConcurrentMapCache 等; • 每次调用需要缓存功能的方法时,Spring会检查检查指定参数的指定的目标方法是否 已经被调用 过;如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法 并缓存结果后返回给用户。下 次调用直接从缓存中获取。 • 使用Spring缓存抽象时我们需要关注以下两点; 1、确定方法需要被缓存 以及他们的缓存策略 2、从缓存中读取之前缓存存储的数据 Cache 缓存接口,定义缓存操作。实现有:RedisCache、EhCacheCache、 ConcurrentMapCache等 CacheManager 缓存管理器,管理各种缓存(Cache)组件 @Cacheable 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存 @CacheEvict 清空缓存 @CachePut 保证方法被调用,又希望结果被缓存。 @EnableCaching 开启基于注解的缓存 keyGenerator 缓存数据时key生成策略 serialize 缓存数据时value序列化策略 @CacheConfig 抽取缓存的公共配置 三、几个重要概念&缓存注解 1、常用注解 2、常用参数 名字 位置 描述 示例 methodName root object 当前被调用的方法名 #root.methodName method root object 当前被调用的方法 #root.method.name target root object 当前被调用的目标对象 #root.target targetClass root object 当前被调用的目标对象类 #root.targetClass args root object 当前被调用的方法的参数列表 #root.args[0] 3、常用参数SPEL说明 名字 位置 描述 示例 caches root object 当前方法调用使用的缓存列表(如 @Cacheable(value= {"cache1","cache2"}) ), 则有两 个cache #root.caches[0].name argument name evaluation context 方法参数的名字. 可以直接 #参数 名 ,也可以使用 #p0或#a0 的形 式,0代表参数的索引; #iban 、 #a0 、 #p0 result evaluation context 方法执行后的返回值(仅当方法执 行之后的判断有效,如‘unless’ , ’cache put’的表达式 ’cache evict’的表达式 beforeInvocation=false ) #result 四、代码中使用缓存 1、搭建基本环境 1、导入数据库文件 创建出department和employee表 2、创建javaBean封装数据 3、整合MyBatis操作数据库 1.配置数据源信息 2.使用注解版的MyBatis; 1)、@MapperScan指定需要扫描的mapper接口所在的包
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值