shiro简单使用

环境搭建

  • 新建springboot项目,勾选web,thymeleaf
  • 编写前端页面
    • index.html
    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <h1>首页</h1>
    <hr>
    <a th:href="@{/user/add}">add</a>
    <br>
    <a th:href="@{/user/update}">update</a>
    </body>
    </html>	
    
    • user/add.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <h1>add</h1>
    </body>
    </html>
    
    • user/update.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <h1>update</h1>
    </body>
    </html>
    
  • 编写对应的controller
    @Controller
    public class MyController {
    
        @RequestMapping({"/","/index"})
        public String toIndex(){
            return "index";
        }
    
        @RequestMapping("/user/add")
        public String toAdd(){
            return "user/add";
        }
    
        @RequestMapping("/user/update")
        public String toUpdate(){
            return "user/update";
        }
    
    }
    
  • 启动项目,页面间正常跳转

登录拦截

UserRealm

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

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

自定义shiro配置

@Configuration
public class ShiroConfig {


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

        /*
        * 添加过滤器
        * anon: 无需认证即可访问
        * authc: 认证成功才能访问
        * user: 开启记住账号功能才能使用
        * */
        Map<String, String> map = new LinkedHashMap<>();
        // user下的所有url 都需要认证
//        map.put("/user/add","authc");
//        map.put("/user/update","authc");
        map.put("/user/**","authc");
        bean.setFilterChainDefinitionMap(map);
		
		// 设置登录页
        bean.setLoginUrl("/toLogin");
        return bean;

    }

    /**
     * @param userRealm
     * @return  DefaultWebSecurityManager
     */
    @Bean("securityManager")
    public DefaultWebSecurityManager securityManager(@Qualifier("userRealm") UserRealm userRealm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(userRealm);
        return securityManager;
    }


    /**
     * @return Realm
     */
    @Bean("userRealm")
    public UserRealm userRealm(){
        return new UserRealm();
    }
}
  1. 数据验证,注入到容器
  2. 安全管理,set数据(数据验证对象),注入到容器
  3. 过滤器,set数据(安全管理对象),注入到容器

启动访问,点击add,update 按钮都会被拦截,然后跳转到登录页

用户认证

编写登录页面

login.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">

<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>登录</h1>
<hr>
<p th:text="${msg}" style="color: red"></p>
<form th:action="@{/login}" method="post">
    username:<input type="text" name="username"><br>
    password:<input type="password" name="password"><br>
    <input type="submit" value="login">
</form>
</body>
</html>

controller中添加对应的方法

@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);
        // 没有异常就登录成功,跳转到首页
        return "index";
    } catch (UnknownAccountException e) {
        model.addAttribute("msg","用户名错误");
        // 登录失败,跳转到首页,显示错误信息
        return "login";
    } catch (IncorrectCredentialsException e) {
        model.addAttribute("msg","密码错误");
        return "login";
    }
}

数据验证

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

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

        // 模拟数据库中的用户名和密码
        String usr = "root";
        String pwd = "root";

        // 用户名校验
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        // 如果token中的用户名和数据库中的密码不一样就返回用户名错误
        if (!token.getUsername().equals(usr)) {
            // 抛出异常 UnknownAccountException
            return null;
        }

        // 密码校验 传递给shiro进行校验 不用自己校验
        return new SimpleAuthenticationInfo("", pwd, "");

    }
}

启动访问,点击add,update 按钮都会被拦截,然后跳转到登录页,输入 root,root 才能登录成功

shiro整合mybatis

  • 导入依赖

    	<dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.20</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/log4j/log4j -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.22</version>
        </dependency>
        <!--引入mybatis,这是Mybatis官方提供的适配SpringBoot的,而不是SpringBoot自己的-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.1</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
    
  • 编写实体类

    User

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
        private Integer id;
        private String name;
        private String pwd;
    }
    
  • 编写mapper

    UserMapper

    @Repository
    @Mapper
    public interface UserMapper {
    
        User queryUserByName(String name);
    
    }
    
  • 编写mapper.xml

    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.starry.mapper.UserMapper">
    
        <select id="queryUserByName" resultType="user">
            select * from mybatis.user where name = #{name};
        </select>
    </mapper>
    
  • 编写service

    UserService

    public interface UserService {
    
        User queryUserByName(String name);
    }
    

    UserServiceImpl

    @Service
    public class UserServiceImpl implements UserService {
    
        @Autowired
        UserMapper userMapper;
    
        @Override
        public User queryUserByName(String name) {
            return userMapper.queryUserByName(name);
        }
    
    }
    
  • 添加配置

    properties.yml

    spring:
      datasource:
        username: root
        password: root
        #?serverTimezone=UTC解决时区的报错
        url: jdbc:mysql://localhost:3306/mybatis?userSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
        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
    

    application.properties

    #指定Mybatis的Mapper文件
    mybatis.mapper-locations=classpath:mapper/*.xml
    #指定Mybatis的实体目录 起别名
    mybatis.type-aliases-package=com.starry.pojo
    
  • 修改方法
    UserRealm的 认证 方法

    	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            System.out.println("执行了认证=>doGetAuthorizationInfo");
    
            // 模拟数据库中的用户名和密码
    //        String usr = "root";
    //        String pwd = "root";
    
            UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
            // 用户名校验
            User user = userService.queryUserByName(token.getUsername());
            if (user == null) {
                return null;
            }
    
            // 如果token中的用户名和数据库中的密码不一样就返回用户名错误
            /*if (!token.getUsername().equals(usr)) {
                // 抛出异常 UnknownAccountException
                return null;
            }*/
    
            // 密码校验 传递给shiro进行校验 不用自己校验
    //        return new SimpleAuthenticationInfo("", pwd, "");
            return new SimpleAuthenticationInfo("", user.getPwd(), "");
    
        }
    
  • 启动服务,数据库中的用户都能登录

请求授权

从数据库中获取权限

修改数据库添加权限字段
shiro简单使用
修改实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Integer id;
    private String name;
    private String pwd;
    private String perms;
}

shiro过滤方法 添加过滤规则

// 授权 拥有user:add权限的用户才能访问 /user/add 资源
map.put("/user/add", "perms[user:add]");
map.put("/user/update", "perms[user:update]");
// 设置没有权限跳转的url
bean.setUnauthorizedUrl("/unauth");

修改 认证 方法

// 传入principal对象为 user  授权方法即可使用user
 return new SimpleAuthenticationInfo(user, user.getPwd(), "");

编写授权方法

@Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行了授权=>doGetAuthorizationInfo");

        // 获取当前的登录对象
        Subject subject = SecurityUtils.getSubject();
        User currentUser = (User) subject.getPrincipal();
        // 如果权限不是null就赋予对应的权限
        if (currentUser.getPerms()!=null){
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            // 赋予权限
            info.addStringPermission(currentUser.getPerms());

            return info;
        }
        return null;

    }

编写controller

@RequestMapping("/unauth")
@ResponseBody
public String unauthorized(){
    return "未授权,无法访问";
}

@RequestMapping("/logout")
public String logout(){
    Subject subject = SecurityUtils.getSubject();
    subject.logout();
    return "/login";
}

shiro整合thymeleaf

导入依赖

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

ShiroConfig

/**
* @return shiro-thymeleaf方言
*/
@Bean
public ShiroDialect getShiroDialect(){
   return new ShiroDialect();
}

index.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>
<h1>首页</h1>
<!-- 如果未认证就显示登录 -->
<div shiro:notAuthenticated="">
    <a th:href="@{/toLogin}">登录</a>
</div>
<!-- 如果已经认证就显示退出 -->
<div shiro:authenticated="">
    <a th:href="@{/logout}">退出</a>
</div>
<hr>

<!-- 如果拥有权限 user:add 就显示 -->
<div shiro:hasPermission="user:add">
    <a th:href="@{/user/add}">add</a>
</div>
<br>
<!-- 如果拥有权限 user:update 就显示 -->
<div shiro:hasPermission="user:update">
    <a th:href="@{/user/update}">update</a>
</div>
</body>
</html>

启动服务,点击 登录

  • 输入 starry,starry ,只显示 add 按钮
  • 输入 root,root ,只显示 update 按钮
  • 输入 guest,guest ,不显示按钮

项目结构

shiro简单使用
项目下载: 点击下载

上一篇:java安全框架shiro(一)


下一篇:【Shiro】三、Shiro实现自定义密码验证规则