Spring Boot中集成Shiro

1.shiro简介

Apache Shiro是一个功能强大且灵活的开源安全框架,可以干净地处理身份验证,授权,企业会话管理和加密。

2.shiro功能介绍

Apache Shiro是具有许多功能的全面的应用程序安全框架。下图显示了Shiro核心功能

img

Shiro以Shiro开发团队所谓的“应用程序安全性的四个基石”为目标-身份验证,授权,会话管理和密码术:

  • **身份验证:**有时称为“登录”,这是证明用户就是他们所说的身份的行为。
  • **授权:**访问控制的过程,即确定“谁”有权访问“什么”。
  • **会话管理:**即使在非Web或EJB应用程序中,也可以管理用户特定的会话。
  • **密码术:**使用密码算法保持数据安全,同时仍然易于使用。
3.shiro核心组件
  • Subjectorg.apache.shiro.subject.Subject
    当前与软件交互的实体(用户,第三方服务,计划任务等)的特定于安全性的“视图”。(把操作交给SecurityManager)
  • SecurityManagerorg.apache.shiro.mgt.SecurityManager
    安全管理器(关联Realm)
  • Realm(org.apache.shiro.realm.Realm
    领域充当Shiro与应用程序的安全数据之间的“桥梁”或“连接器”。当真正需要与安全性相关的数据(如用户帐户)进行交互以执行身份验证(登录)和授权(访问控制)时,Shiro会从为应用程序配置的一个或多个Realms中查找许多此类内容。您可以根据Realms需要配置任意数量(通常每个数据源一个),并且Shiro会根据需要进行协调,以进行身份验证和授权。
4.shiro实战
4.1 环境构建

1.maven依赖:

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

2.自定义Realm

自定义 realm 需要继承 AuthorizingRealm 类,因 为该类封装了很多方法,它也是一步步继承自 Realm 类的,继承了 AuthorizingRealm 类后,需要重写 两个方法:

doGetAuthenticationInfo() 方法:用来验证当前登录的用户,获取认证信息

doGetAuthorizationInfo() 方法:用来为当前登陆成功的用户授予权限和角色

public class MyRealm extends AuthorizingRealm {
    @Resource
    UserService userService;

    @Override
    //用来为当前登陆成功的用户授予权限和角色
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

        return null;
    }

    @Override
    //用来验证当前登录的用户,获取认证信息
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
       
            return null;
       

    }

}

后续实现具体功能再对这两个方法进行具体实现和讲解;

3.shiro配置文件

1.ShiroFileterFactoryBean : 可以进行权限设置,访问控制等功能,由DefaultWebSecurityManager 对设置进行实现;

2.DefaultWebSecurityManager :根据自定义的Realm进行安全管理;

3.自定义Realm: 自定义验证规则;

@Configuration
public class ShiroConfig {
/*
1.ShiroFileterFactoryBean
* */
@Bean
public ShiroFilterFactoryBean getshShiroFilterFactoryBean(@Qualifier("securityManager") DefaultSecurityManager securityManager) {
    ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();
    //添加shiro内置过滤器
    /*
    * 常用过滤器:
    *   anon:无需认证(登录)可以访问
    *   authc:必须认证才可以访问
    *   user: 如果使用remmemberMe的功能可以直接访问
    *   perms: 该资源必须得到资源权限才可以访问
    *   roles:该资源必须得到角色权限才可以访问
    * */

    //配置过滤规则
    Map<String,String> filter=new LinkedHashMap<>();
    filter.put("/user/add","anon");
    filter.put("/user/update","authc");
    //放行hello.html
    filter.put("/hello.html","anon");
    //拦截所有请求包括html也会被拦截
    filter.put("/*","authc");
    //将过滤规则设置到shiroFilterFactoryBean
    shiroFilterFactoryBean.setFilterChainDefinitionMap(filter);
    //访问被拦截后访问的地址,如/user/update被拦截时就会访问/user/toLogin;
    shiroFilterFactoryBean.setLoginUrl("/user/toLogin");
    shiroFilterFactoryBean.setSecurityManager(securityManager);
    return shiroFilterFactoryBean;
}

/*
* 2.DefaultWebSecurityManager
* */
@Bean(name = "securityManager")
public DefaultSecurityManager getDefaultSecurityManager(@Qualifier("MyRealm")Realm myRealm) {
    DefaultSecurityManager securityManager = new DefaultWebSecurityManager(myRealm);
    return securityManager;
}

/*
* 3.自定义Realm
* */
@Bean(name = "MyRealm")
public Realm getRealm() {
    return new MyRealm();
}
}

配置的过滤规则只有访问controller层进行跳转时才能生效,直接访问xxx.html页面是不生效的,也就是说即使你配置了拦截/add,也可以访问add.html;

4.2 登入验证

1.创建数据库表

暂时只用到用户表,角色表和权限表用于后续授权;

#角色表
CREATE TABLE `t_role` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键', `rolename` varchar(20) DEFAULT NULL COMMENT '角色名称', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8

#用户表
CREATE TABLE `t_user` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户主键', `username` varchar(20) NOT NULL COMMENT '用户名', `password` varchar(20) NOT NULL COMMENT '密码' ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8


#权限表
CREATE TABLE `t_permission` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键', `permissionname` varchar(50) NOT NULL COMMENT '权限名', `role_id` int(11) DEFAULT NULL COMMENT '外键关联role', PRIMARY KEY (`id`), KEY `role_id` (`role_id`), CONSTRAINT `t_permission_ibfk_1` FOREIGN KEY (`role_id`) REFERENCES `t_role` (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8

#用户与权限表
CREATE TABLE `user_role` (
  `user_id` int(11) DEFAULT NULL,
  `role_id` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2.自定义Realm的doGetAuthenticationInfo方法添加具体实现

public class MyRealm extends AuthorizingRealm {
    @Resource
    UserService userService;

    @Override
    //用来为当前登陆成功的用户授予权限和角色
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

        return null;
    }

    @Override
    //用来验证当前登录的用户,获取认证信息
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        // 根据token获取用户名,此Token是controller层穿来的,具体看下一步
        String userName = (String) authenticationToken.getPrincipal();
        // 根据用户名从数据库中查询该用户
        User user = userService.getByUsername(userName);
        if (user != null) {
     //返回SimpleAuthenticationInfo来验证密码,只有user.getPassword()为必填参数,其他的可以填“”;         //验证失败 subject.login(token);方法会出现IncorrectCredentialsException异常      
            return SimpleAuthenticationInfo(user.getUserName(), user.getPassword(), "MyRealm");
        } else {
            //返回null subject.login(token)方法会报UnknownAccountException异常;
            return null;
        }

    }

}

3.编写controller

subject.login(token)这一步会带着存有前端传来的用户信息去自定义的Realm中的doGetAuthenticationInfo验证用户信息;

@Controller
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/login")
    public String login(User user, Model model) {
        if (user == null) {
            return "user/login";
        } else {
            Subject subject = SecurityUtils.getSubject();
            UsernamePasswordToken token=new UsernamePasswordToken(user.getUserName(), user.getPassword());
            try {
                subject.login(token);
            } catch (UnknownAccountException e) {
                model.addAttribute("msg", "用户不存在");
                return "user/login";
            } catch (IncorrectCredentialsException e) {
                model.addAttribute("msg","密码错误");
               return  "user/login";
            }
        }
        return "success";
    }
}

4.前端代码login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<p style="color: red" th:text="${msg}"></p>
<form action="/user/login" >
    <p>请输入userName:</p>
    <input type="text" name="userName">
    <p>请输入password:</p>
    <input type="text" name="password" >
    <input type="submit" value="submit">
</form>
</body>
</html>

5.测试

输入不存在用户时:
在这里插入图片描述

输入错误密码时:

在这里插入图片描述

输入正确密码:

在这里插入图片描述

登录验证成功后 shiroConfig中getshShiroFilterFactoryBean方法所配置的需要验证才能访问的请求(authc)就都能访问了;

4.3 添加角色和添加授权

数据库表结构:

t_user:

“id” “username” “password”
“1” “yg1” “a”
“2” “yg2” “a”
“3” “yg3” “a”

t_role

“id” “rolename”
“1” “admin”
“2” “boss”
“3” “user”

t_permission

“id” “permissionname” “role_id”
“1” “user:*” “1”
“2” “user:add” “2”
“3” “user:update” “3”

user_role

“u_id” “r_id”
“1” “1”
“2” “2”
“3” “3”

1.userMapper接口的编写

public interface UserMapper {
    public Set<Role> getRoles(String userName);
    public Set<Permission> getPermissions(String userName);
    public User getByUsername(String userName);
}

2.userMapper.xml的编写

<!--     public User getByUsername(String userName);-->
    <select id="getByUsername" resultType="User">
   select * from t_user where username=#{userName}
    </select>

    <!--     public Set<Role> getRoles(String userName);-->
    <select id="getRoles" resultType="Role">
    SELECT tr.* from t_role tr,user_role ur where
    tr.id=ur.r_id and ur.u_id in (select id from
   t_user where username=#{userName})
    </select>

    <!--    public Set<Permission> getPermissions(String userName);-->
    <select id="getPermissions" resultType="Permission">
SELECT tp.* from t_permission tp,user_role ur
  where tp.role_id=ur.r_id AND ur.u_id in (select id
	 from t_user where username=#{userName})
    </select>

3.userService编写

@Service
public  class UserService {

    @Resource
    UserMapper userMapper;
    public Set<Role> getRoles(String userName) {
        if (userName == "" || userName == null) {
            return null;
        }
        Set<Role> roleList = userMapper.getRoles(userName);
        return roleList;
    }


    public Set<Permission>getPermissions(String userName) {
        if (userName == "" || userName == null) {
            return null;
        }
        Set<Permission> permissions = userMapper.getPermissions(userName);
        return permissions;
    }
    public User getByUsername(String userName) {
        if (userName == "" || userName == null) {
            return null;
        }
        return userMapper.getByUsername(userName);
    }
}

4.自定义Realm

AuthorizationInfo方法中添加角色和添加授权;

  @Override
    //用来为当前登陆成功的用户授予权限和角色
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("授权操作");
                User user = (User) principalCollection.getPrimaryPrincipal();
                String userName=user.getUserName();
       SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        //添加角色
       Set<Role> roles = userService.getRoles(userName);
        //将roles转为set<String>roleNames
        Set<String>roleNames=new HashSet<>();
        for (Role role : roles) {
          roleNames.add(role.getRoleName());
        }
        authorizationInfo.setRoles(roleNames);

        //添加授权
        Set<Permission> permissionSet = userService.getPermissions(userName);
        //将roles转为set<String>roleNames
        Set<String>permissions=new HashSet<>();
        for (Permission permission : permissionSet) {
            permissions.add(permission.getPermissionName());
        }
        authorizationInfo.setStringPermissions(permissions);


        return authorizationInfo;
    }

5.shiroConfig配置文件

在shiroConfig的ShiroFilterFactoryBean方法中添加角色认证和权限认证;

filter.put("/user/add",“perms[user:add]”);

filter.put("/user/update",“roles[admin,boss]”);

@Bean
public ShiroFilterFactoryBean getshShiroFilterFactoryBean(@Qualifier("securityManager") DefaultSecurityManager securityManager) {
    ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();
    shiroFilterFactoryBean.setSecurityManager(securityManager);
    //添加shiro内置过滤器
    /*
    * 常用过滤器:
    *   anon:无需认证(登录)可以访问
    *   authc:必须认证才可以访问
    *   user: 如果使用remmemberMe的功能可以直接访问
    *   perms: 该资源必须得到资源权限才可以访问
    *   roles:该资源必须得到角色权限才可以访问
    * */

    Map<String,String> filter=new LinkedHashMap<>();
    //filter.put("/user/add","anon");
    //filter.put("/user/update","authc");

    //授权过滤器
    filter.put("/user/add","perms[user:add]");
    filter.put("/user/update","roles[user,boss]");
    //未授权就跳转到该页面
    shiroFilterFactoryBean.setUnauthorizedUrl("/user/unAuth");
    //拦截所有请求包括html也会被拦截
   // filter.put("/*","authc");
    shiroFilterFactoryBean.setFilterChainDefinitionMap(filter);
    shiroFilterFactoryBean.setLoginUrl("/user/toLogin");
    shiroFilterFactoryBean.setSecurityManager(securityManager);
    return shiroFilterFactoryBean;
}

测试:

yg1有user:权限,yg2中有user:add权限所以可以访问*/user/add**,yg3没有这些权限不能访问,yg1没有被授权user或boss角色所以不能访问/user/update而yg2被授权boss,yg3被授权user所以能访问;

5 thymeleaf整合shiro
1.导入依赖
 <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>
2.shiroConfig配置类添加bean
   @Bean
    public ShiroDialect getShiroDialect() {
        return new ShiroDialect();
    }

3.编写前端代码

xmlns:shiro=“http://www.pollix.at/thymeleaf/shiro” 导入后才能有shiro标签提示;

**xmlns:th=“http://www.thymeleaf.org” ** 导入后才能有thymeleaf标签提示;

shirol:标签后面的条件成立才会显示该标签的内容

常用shirol:标签解释

shiro:guest="" 验证当前用户是否为“访客”,即未认证(包含未记住)的用户。

<p shiro:guest="">Please <a href="login.html">login</a></p>

shiro:user="" 认证通过或已记住的用户

<p shiro:user="">Welcome back </p>

shiro:authenticated="" 已认证通过的用户。不包含已记住的用户,这是与user标签的区别所在

<a shiro:authenticated="" href="update.html">Update your  information</a>

shiro:principal 输出当前用户信息,通常为登录帐号信息。

<p>Hello, <shiro:principal/>, how are you today?</p>

shiro:notAuthenticated="" 未认证通过用户,与authenticated标签相对应。与guest标签的区别是,该标签包含已记住用户。

shiro:hasRole=“xxx” 验证当前用户是否属于xxx角色

<a shiro:hasRole="admin" href="admin.html">Administer the system</a><!-- 拥有该角色 -->

shiro:lacksRole=“xxx” 与hasRole标签逻辑相反,当用户不属于该角色时验证通过

shiro:hasAllRoles=“xx1, xx2” 验证当前用户是否属于以下所有角色

shiro:hasAnyRoles="xx1, xx2 " 验证当前用户是否属于以下任意一个角色

shiro:hasPermission ="" 与Role的使用相同也有对应的lacksPermission等;

<!DOCTYPE html>
<html lang="en"
      xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"
      xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<p th:text="${msg}"></p>
<div shiro:hasPermission="user:update">
<a href="/user/update">更新:</a>
</div>

<div shiro:hasPermission="user:add">
    <a href="add">添加:</a>
</div>

</body>
</html>

测试结果:

*yg1有user:权限所以都能显示,yg2只有user:add权限只能显示添加,yg2只有user:update权限只能显示更新;

只有经过controller层进行模板解析后跳转的html页面shiro标签才会起效;

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
课程简介:历经半个多月的时间,Debug亲自撸的 “企业员工角色权限管理平台” 终于完成了。正如字面意思,本课程讲解的是一个真正意义上的、企业级的项目实战,主要介绍了企业级应用系统后端应用权限的管理,其主要涵盖了六大核心业务模块、十几张数据库表。 其的核心业务模块主要包括用户模块、部门模块、岗位模块、角色模块、菜单模块和系统日志模块;与此同时,Debug还亲自撸了额外的附属模块,包括字典管理模块、商品分类模块以及考勤管理模块等等,主要是为了更好地巩固相应的技术栈以及企业应用系统业务模块的开发流程! 核心技术栈列表: 值得介绍的是,本课程在技术栈层面涵盖了前端和后端的大部分常用技术,包括Spring BootSpring MVC、Mybatis、Mybatis-Plus、Shiro(身份认证与资源授权跟会话等等)、Spring AOP、防止XSS攻击、防止SQL注入攻击、过滤器Filter、验证码Kaptcha、热部署插件Devtools、POI、Vue、LayUI、ElementUI、JQuery、HTML、Bootstrap、Freemarker、一键打包部署运行工具Wagon等等,如下图所示: 课程内容与收益: 总的来说,本课程是一门具有很强实践性质的“项目实战”课程,即“企业应用员工角色权限管理平台”,主要介绍了当前企业级应用系统员工、部门、岗位、角色、权限、菜单以及其他实体模块的管理;其,还重点讲解了如何基于Shiro的资源授权实现员工-角色-操作权限、员工-角色-数据权限的管理;在课程的最后,还介绍了如何实现一键打包上传部署运行项目等等。如下图所示为本权限管理平台的数据库设计图: 以下为项目整体的运行效果截图: 值得一提的是,在本课程,Debug也向各位小伙伴介绍了如何在企业级应用系统业务模块的开发,前端到后端再到数据库,最后再到服务器的上线部署运行等流程,如下图所示:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值