SpringBoot 集成 Shiro

SpringBoot 集成 Shiro

1、框架搭建

​ 创建SpringBoot项目,集成Shiro组件。打开idea工具。进入File->New->Project选择Spring Initializr

20190303104116439.png
包命自己命名。项目创建好后,修改`pom.xml`文件,引入Shiro和Thymeleaf。pom.xml文件如下:
<?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 http://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.1.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>cn.org.july.shiro</groupId>
    <artifactId>springboot-shiro</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-shiro</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>

        <!-- 2 引入thymeleaf依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <!-- 3 引入shiro-spring 依赖-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </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>
        </dependency>
    </dependencies>

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

</project>

2、 新建模版文件

​ 在resources目录下新建templates文件夹,在templates文件夹中新建test.html文件和user文件夹,在user文件夹中新建add.html和updtae.html. 项目结构如下:

20190303105309038.png

test.html 内容如下:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>测试Thymeleaf</title>
</head>
<body>
<h3 th:text="${test}"></h3>
<hr/>
进入用户添加页面:<a href="add">用户添加</a>
<br>
进入用户修改页面:<a href="update">用户修改</a>
</body>
</html>

add.html内容如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户添加页面</title>
</head>
<body>
<h1>用户添加页面</h1>
</body>
</html>

update.html 页面内容如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户修改页面</title>
</head>
<body>
<h1>用户修改页面</h1>
</body>
</html>

​ 在项目中新增 UserController 包名自己起名即可。内容如下:

package cn.org.july.spring.shiro.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class UserController {

    @RequestMapping(value = "/hello")
    @ResponseBody
    public String hello() {
        return "ok";
    }
    @RequestMapping(value = "/testThymeleaf")
    public String testThymeleaf(Model model) {
        //把数据存入model
        model.addAttribute("test", "测试Thymeleaf");
        //返回test.html
        return "test";
    }

    @RequestMapping(value = "/add")
    public String add(Model model) {
        //把数据存入model
        model.addAttribute("test", "添加用户页面");
        //返回test.html
        return "/user/add";
    }
    
    @RequestMapping(value = "/update")
    public String update(Model model) {
        //把数据存入model
        model.addAttribute("test", "修改用户页面");
        //返回test.html
        return "/user/update";
    }
    
    @RequestMapping(value = "/toLogin")
    public String toLogin(){
        return "login";
    }
}

​ 启动项目,访问testThymeleaf页面,在浏览器中输入http://127.0.0.1:8080/testThymeleaf ,点击两个连接。效果如下图:

01.gif

3、配置Shiro

​ 在现有工程中新建两个包分别是configrealm两个包。在realm包中新建UserRealm类,该类实现用户认证和权限授权。内容先简单构建一下,

public class UserRealm extends AuthorizingRealm {
    /**
     * 执行授权逻辑
     *
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.printf("用户授权");
        return null;
    }

    /**
     * 执行认证逻辑
     *
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.printf("用户认证");
        return null;
    }
}

​ 在config包中新建ShiroConfig类。内容如下:

/**
 * shiro 的配置类
 */
@Configuration
public class ShiroConfig {
    /**
     * 创建shiroFilterFactoryBean
     */
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
        //配置Shiro过滤器
        /**
         * 内置Shiro过滤器实现相关拦截功能
         *      常用的过滤器有:
         *          anon  : 无需认证(登录)可以访问
         *          authc : 必须认证才能访问
         *          user  : 如果使用rememberMe的功能可以直接访问
         *          perms : 该资源必须得到资源访问权限才可以使用
         *          role  : 该资源必须得到角色授权才可以使用
         */
        Map<String, String> filterMap = new LinkedHashMap<>();
        filterMap.put("/testThymeleaf", "anon");
        filterMap.put("/*", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
        shiroFilterFactoryBean.setLoginUrl("/toLogin");
        return shiroFilterFactoryBean;
    }

    /**
     * 创建DefaultWebSecurityManager
     */
    @Bean
    public DefaultWebSecurityManager getDefaultWebSecurityManager(UserRealm userRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(userRealm);
        return securityManager;
    }

    /**
     * 创建Realm
     */
    @Bean
    public UserRealm getRealm() {
        return new UserRealm();
    }

}

​ 配置完成后,启动项目,访问测试页面,点击添加用户连接和修改用户连接。效果如下图。

[图片上传失败...(image-860d7-1551622661543)]

​ 我们可以看到,当我们访问用户添加和用户修改时,会跳转到登录页面。我们配置的Shiro的访问规则如下:

Map<String, String> filterMap = new LinkedHashMap<>();
filterMap.put("/testThymeleaf", "anon");// 不拦截测试页面
filterMap.put("/*", "authc"); //拦截所有请求

​ 当我们点击用户添加和用户修改时,Shiro拦截了我们的请求,通过设置shiroFilterFactoryBean.setLoginUrl("/toLogin");将拦截后的请求重定向到/toLogin页面。

4、用户认证

1、完善login.html页面

​ 新增form表单,实现用户名密码登录过程。完善后内容如下:

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
</head>
<body>
<h1>登录页面</h1>
<hr/>
<h3 th:text="${msg}" style="color: red"></h3>
<form method="post" action="login">
    用户名 :<input type="text" name="username"><br>
    密 码 :<input type="password" name="password"><br>
    <input type="submit" value="登录">
</form>
</body>
</html>

2、完善UserController

    新增`login`用户登录处理方法:
@RequestMapping(value = "/login")
    public String login(String username, String password, Model model) {
        System.out.printf("username :" + username);
        //1、获取 Subject
        Subject subject = SecurityUtils.getSubject();
        //2、封装用户数据
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);
        //3、执行登录方法
        try {
            subject.login(usernamePasswordToken);
            return "redirect:/testThymeleaf";
        } catch (UnknownAccountException e) {//该异常用户名称不存在
            //登录失败,用户名称不存在
            model.addAttribute("msg", "用户名称不存在");
            return "login";
        } catch (IncorrectCredentialsException e) {//该异常密码错误
            //登录失败,密码错误
            model.addAttribute("msg", "密码错误");
            return "login";
        }
    }

3、修改UserRealm 完善用户认证逻辑。

public class UserRealm extends AuthorizingRealm {
    /**
     * 执行授权逻辑
     *
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.printf("用户授权");
        return null;
    }

    /**
     * 执行认证逻辑
     *
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("用户认证开始");
        //1、模拟从数据库获取 用户名称和密码
        String usernameByDB = "admin";
        String passwordByDb = "admin";

        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;
        //2、判断用户名称是否存在
        if (!usernameByDB.equals(usernamePasswordToken.getUsername())) {
            //用户名称不存在,Shiro底层会抛出UnknowAccountException
            return null;
        }
        //3、判断密码是否正确
        return new SimpleAuthenticationInfo("", passwordByDb, "");
    }
}

​ 注意:修改后检查ShiroConfig中过滤规则是否对/login 请求开放,如没有开放请求无法到达服务端。修改过滤规则。

Map<String, String> filterMap = new LinkedHashMap<>();
        filterMap.put("/testThymeleaf", "anon");
        filterMap.put("/login", "anon");
        filterMap.put("/*", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);

​ 修改完成后,重启服务。

03.gif

用户认证功能完成。

5、整合Mybatis 完成用户认证;

​ SpringBoot 整合Mybatis 建立数据库连接,完善DAO,service 层见[Spring Cloud项目搭建] https://blog.csdn.net/July_whj/article/details/85476857 博客说明,不在此处详细讲解。

​ 修改UserRealm 将之前默认的用户名称密码切换到数据库。修改如下:

public class UserRealm extends AuthorizingRealm {
    //注入UserService
    @Autowired
    private UserService userService;

    /**
     * 执行授权逻辑
     *
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.printf("用户授权");
        return null;
    }

    /**
     * 执行认证逻辑
     *
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("用户认证开始");

        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;
        //根据用户名称查看用户信息
        User user = userService.findUserByName(usernamePasswordToken.getUsername());
        //2、判断用户名称是否存在
        if (null == user || !user.getUserName().equals(usernamePasswordToken.getUsername())) {
            //用户名称不存在,Shiro底层会抛出UnknowAccountException
            return null;
        }
        //3、判断密码是否正确
        return new SimpleAuthenticationInfo("", user.getPassword(), "");
    }
}

效果如下:

6、资源授权

1、新增资源访问权限配置

​ 修改ShiroConfig 新增资源访问权限。

/**
 * 授权资源
 */
        filterMap.put("/add", "perms[user:add]");//访问 /add时需要user:add的权限
        filterMap.put("/*", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
        shiroFilterFactoryBean.setUnauthorizedUrl("/noAuth");//如果没权限跳转到/noAuth请求
        shiroFilterFactoryBean.setLoginUrl("/toLogin");

2、新增noAuth.html 页面,如果没有权限访问,跳转至该页面。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>授权失败</title>
</head>
<body>
<h1>没有权限,无法访问该页面。</h1>
</body>
</html> 

​ 重启服务后访问如下:

05.gif

3、使用shiro标签控制前台显示控件

​ 1、新增pom依赖,thymeleaf 扩展 shiro

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

​ 2、修改ShiroConfig 配置

配置ShiroDialect, 用于thymeleaf和Shiro标签配置使用。新增如下配置:

/**
     * 配置ShiroDialect, 用于thymeleaf和Shiro标签配置使用
     */
    @Bean
    public ShiroDialect getShiroDialect() {
        return new ShiroDialect();
    }

​ 3、修改test.html,新增Shiro标签

​ 在test.html 中新增 <div>标签,并使用shiro:hasPermission该标签,判断当前登录用户是否有该权限。没有权限则隐藏该控件。

<!DOCTYPE html> 
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml" xmlns:shiro="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>测试Thymeleaf</title>
</head>
<body>
<h3 th:text="${test}"></h3>
<hr/>
<div shiro:hasPermission="user:add">
    进入用户添加页面:<a href="add">用户添加</a>
</div>
<br>
<div shiro:hasPermission="user:update">
    进入用户修改页面:<a href="update">用户修改</a>
</div>
</body>
</html>

​ 4、修改UserRealm类,修改用户授权

​ 修改授权类,获取当前登录用户权限,并赋值给Shiro。进行权限判定。获取当前登录用户通过doGetAuthenticationInfo方法,如果用户登录成功则将用户通过SimpleAuthenticationInfo对象赋值给Subject对象中principal 属性。我们可以通过以下方法获取当前登录用户信息,并将权限赋值给Shiro。

/**
     * 执行授权逻辑
     *
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.printf("用户授权");
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        Subject subject = SecurityUtils.getSubject();
        User user = (User) subject.getPrincipal();
        simpleAuthorizationInfo.addStringPermission(user.getPerms());
        return simpleAuthorizationInfo;
    }

​ 系统重启,访问浏览器,效果如下:


06.gif

附录:数据库脚本

/*
 Navicat MySQL Data Transfer

 Source Server         : localhost
 Source Server Type    : MySQL
 Source Server Version : 50719
 Source Host           : localhost
 Source Database       : cloudDB01

 Target Server Type    : MySQL
 Target Server Version : 50719
 File Encoding         : utf-8

 Date: 03/03/2019 21:24:28 PM
*/

SET NAMES utf8;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
--  Table structure for `user`
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `userName` varchar(50) DEFAULT NULL,
  `dbSource` varchar(50) DEFAULT NULL,
  `phone` varchar(20) DEFAULT NULL,
  `email` varchar(50) DEFAULT NULL,
  `password` varchar(50) DEFAULT NULL,
  `perms` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;

-- ----------------------------
--  Records of `user`
-- ----------------------------
BEGIN;
INSERT INTO `user` VALUES ('1', 'JULY', 'cloudDB01', '18232533234', 'july@163.com', '123456', 'user:add'), ('2', 'WHJ', 'cloudDB01', '12312312312', '123@qq.com', '123456', 'user:update');
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;

项目源码地址:https://github.com/hongjieWang/SpringBoot-Shiro/tree/master/code/springboot-shiro
Shiro 视频地址:后期附上

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,509评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,806评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,875评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,441评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,488评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,365评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,190评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,062评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,500评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,706评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,834评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,559评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,167评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,779评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,912评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,958评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,779评论 2 354

推荐阅读更多精彩内容