# springboot-shiro **Repository Path**: ilovemo/springboot-shiro ## Basic Information - **Project Name**: springboot-shiro - **Description**: springboot-shiro集成 - **Primary Language**: Java - **License**: GPL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-01-04 - **Last Updated**: 2021-01-04 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 1、认识Shiro > Shiro简介 Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。 - Shiro是一个开源的java安全(权限)框架,它能够实现身份验证、授权、加密和会话管理等功能。 - Shiro是Apache旗下的产品,它的官网是: [Shiro官网: Apache Shiro](http://shiro.apache.org/index.html) - Shiro不依赖任何容器,不仅可以运行在JavaEE环境,也可以运行在JavaSE > Shiro 核心组件 用户、角色、权限 给角色赋予权限,给用户赋予角色 1、UsernamePasswordToken:Shiro 用来封装用户登录信息,使用用户的登录信息来创建令牌 Token。 2、SecurityManage:Shiro 的核心部分,负责安全认证和授权。 3、Suject:Shiro 的一个抽象概念,包含了用户信息。 4、Realm:开发者自定义的模块,根据项目的需求,验证和授权的逻辑全部写在 Realm 中。 5、AuthenticationInfo:用户的角色信息集合,认证时使用。 6、AuthorzationInfo:角色的权限信息集合,授权时使用。 7、DefaultWebSecurityManager:安全管理器,开发者自定义的 Realm 需要注入到 DefaultWebSecurityManager 进行管理才能生效。 8、ShiroFilterFactoryBean:过滤器工厂,Shiro 的基本运行机制是开发者定制规则,Shiro 去执行,具体的执行操作就是由 ShiroFilterFactoryBean 创建的一个个 Filter 对象来完成。 Shiro 的运行机制如下图所示: ![](https://imgconvert.csdnimg.cn/aHR0cDovL2ltZ2Nsb3VkLmR1aXlpLnh5ei8vZGF0YTIwMjAwNjAyMjMwODMwLnBuZw?x-oss-process=image/format,png) # 2、SpringBoot 整合 Shiro(单机) 1、创建 Spring Boot 应用作为父工程,pom.xml如下 ```xml 4.0.0 pom shiro-thymeleaf shiro-jwt org.springframework.boot spring-boot-starter-parent 2.4.1 com.godfrey springboot-shiro 0.0.1-SNAPSHOT springboot-shiro Demo project for Spring Boot 1.8 3.4.1 1.7.0 org.springframework.boot spring-boot-starter-web org.projectlombok lombok true org.springframework.boot spring-boot-starter-test test mysql mysql-connector-java com.baomidou mybatis-plus-boot-starter ${mybatis-plus.version} org.apache.shiro shiro-spring ${shiro-spring.version} org.springframework.boot spring-boot-maven-plugin org.projectlombok lombok ``` 2、创建子模块shiro-thymeleaf,pom.xml如下 ```xml springboot-shiro com.godfrey 0.0.1-SNAPSHOT 4.0.0 shiro-thymeleaf 2.0.0 org.springframework.boot spring-boot-starter-thymeleaf com.github.theborakompanioni thymeleaf-extras-shiro ${thymeleaf-shiro.version} ``` 3、数据库SQL脚本SpringBoot-Shiro.sql ```sql CREATE DATABASE IF NOT EXISTS `test`; USE `test`; CREATE TABLE IF NOT EXISTS `account` ( `id` int NOT NULL AUTO_INCREMENT, `username` varchar(20) DEFAULT NULL, `password` varchar(20) DEFAULT NULL, `perms` varchar(20) DEFAULT NULL, `role` varchar(20) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; INSERT INTO `account` (`id`, `username`, `password`, `perms`, `role`) VALUES (1, 'zs', '123123', '', ''), (2, 'ls', '123123', 'manager', ''), (3, 'ww', '123123', 'manager', 'administrator'); ``` 3、配置文件,application.yml ```yml # 项目相关配置 spring: datasource: # 数据库驱动 driver-class-name: com.mysql.cj.jdbc.Driver # 数据库连接url url: jdbc:mysql://39.106.41.184:3306/test?userSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8 # 用户名 username: root # 密码 password: root123 # Thymeleaf配置 thymeleaf: # 前缀 prefix: classpath:/templates/ # 后缀 suffix: .html # 是否缓存 cache: false # mybatis-plus配置 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl ``` 4、MybatisPlus配置类 ```java package com.godfrey.config; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Configuration; /** * MybatisPlus配置 * * @author godfrey * @since 2020-1-2 */ @Configuration @MapperScan("com.godfrey.mapper") public class MybatisPlusConfig { } ``` 5、实体类 ```java package com.godfrey.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; /** * @author godfrey * @since 2021-01-02 */ @Data @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) @TableName("account") public class Account { private static final long serialVersionUID = 1L; /** * 主键id */ @TableId(value = "id", type = IdType.AUTO) private Integer id; /** * 用户名 */ private String username; /** * 密码 */ private String password; /** * 权限 */ private String perms; /** * 角色 */ private String role; } ``` 6、mapper接口 ```java package com.godfrey.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.godfrey.entity.Account; /** * @author godfrey * @since 2021-01-02 */ public interface AccountMapper extends BaseMapper { } ``` 7、测试数据库能否连通 ```java package com.godfrey.mapper; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; /** * @author godfrey * @since 2021-01-02 */ @SpringBootTest class AccountMapperTest { @Autowired private AccountMapper mapper; @Test void test() { mapper.selectList(null).forEach(System.out::println); } } ``` 8、服务接口 ```java package com.godfrey.service; import com.godfrey.entity.Account; /** * @author godfrey * @since 2021-01-02 */ public interface AccountService { Account findByUserName(String userName); } ``` 9、服务实现 ```java package com.godfrey.service.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.godfrey.entity.Account; import com.godfrey.mapper.AccountMapper; import com.godfrey.service.AccountService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @author godfrey * @since 2021-01-02 */ @Service public class AccountServiceImpl implements AccountService { private final AccountMapper accountMapper; @Autowired public AccountServiceImpl(AccountMapper accountMapper) { this.accountMapper = accountMapper; } @Override public Account findByUserName(String username) { QueryWrapper wrapper = new QueryWrapper<>(); wrapper.eq("username", username); return accountMapper.selectOne(wrapper); } } ``` 10、测试Service是否有误 ```java package com.godfrey.service; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; /** * @author godfrey * @since 2021-01-02 */ @SpringBootTest class AccountServiceTest { @Autowired private AccountService accountService; @Test void findByUserName() { System.out.println(accountService.findByUserName("ls")); } } ``` 11、自定义 Shiro 过滤器AccountRealm ```java package com.godfrey.realm; import com.godfrey.entity.Account; import com.godfrey.service.AccountService; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.*; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.subject.Subject; import org.springframework.beans.factory.annotation.Autowired; import java.util.HashSet; import java.util.Set; /** * @author godfrey * @since 2021-01-02 */ public class AccountRealm extends AuthorizingRealm { @Autowired private AccountService accountService; /** * 授权 * * @param principalCollection * @return org.apache.shiro.authz.AuthorizationInfo */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { //获取当前登录的用户信息 Subject subject = SecurityUtils.getSubject(); Account account = (Account) subject.getPrincipal(); //设置角色 Set roles = new HashSet<>(); roles.add(account.getRole()); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles); //设置权限 info.addStringPermission(account.getPerms()); return info; } /** * 认证 * * @param authenticationToken * @return org.apache.shiro.authc.AuthenticationInfo */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken; Account account = accountService.findByUserName(token.getUsername()); if (null != account) { return new SimpleAuthenticationInfo(account, account.getPassword(), getName()); } return null; } } ``` 12、Shiro配置类 ```java package com.godfrey.config; import at.pollux.thymeleaf.shiro.dialect.ShiroDialect; import com.godfrey.realm.AccountRealm; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.LinkedHashMap; import java.util.Map; /** * @author godfrey * @since 2021-01-02 */ @Configuration public class ShiroConfig { /** * 第一步:创建Realm对象 * * @param * @return com.godfrey.realm.AccountRealm */ @Bean public AccountRealm accountRealm() { return new AccountRealm(); } /** * 第二步:创建DefaultWebSecurityManager,Realm对象放入DefaultWebSecurityManager * * @param accountRealm * @return org.apache.shiro.web.mgt.DefaultWebSecurityManager */ @Bean(name = "securityManager") public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("accountRealm") AccountRealm accountRealm) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(accountRealm); return securityManager; } /** * 第三步:创建ShiroFilterFactoryBean,DefaultWebSecurityManager对象放入hiroFilterFactoryBean * * @param securityManager * @return org.apache.shiro.spring.web.ShiroFilterFactoryBean */ @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) { ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean(); factoryBean.setSecurityManager(securityManager); //权限设置 Map filterMap = new LinkedHashMap<>(); filterMap.put("/main","authc"); filterMap.put("/manager","perms[manager]"); filterMap.put("/administrator","roles[administrator]"); factoryBean.setFilterChainDefinitionMap(filterMap); //设置登录页面 factoryBean.setLoginUrl("/login"); //设置未授权页面 factoryBean.setUnauthorizedUrl("/unauth"); return factoryBean; } /** * ShiroDialect(方言):shiro整合thymeleaf * * @param * @return at.pollux.thymeleaf.shiro.dialect.ShiroDialect */ @Bean public ShiroDialect shiroDialect() { return new ShiroDialect(); } } ``` 13、搭建三个页面(main.html,manager.html,administrator.html) ```html Title

main

``` ```html Title

manager

``` ```html Title

administrator

``` 14、编写controller ```java package com.godfrey.controller; import com.godfrey.entity.Account; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.IncorrectCredentialsException; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.subject.Subject; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; /** * @author godfrey * @since 2021-01-03 */ @Controller public class AccountController { @GetMapping("/{url}") public String redirect(@PathVariable("url") String url) { return url; } @PostMapping("/login") public String login(String username, String password, Model model) { //获取当前用户 Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken(username, password); //去到Realm认证方法 try { subject.login(token); Account account = (Account) subject.getPrincipal(); subject.getSession().setAttribute("account", account); return "index"; } catch (UnknownAccountException e) { e.printStackTrace(); model.addAttribute("msg", "用户名错误!"); return "login"; } catch (IncorrectCredentialsException e) { e.printStackTrace(); model.addAttribute("msg", "密码错误!"); return "login"; } } @GetMapping("/unauth") @ResponseBody public String unauth() { return "未授权无法访问此页面!"; } @GetMapping("/logout") public String logout(){ Subject subject = SecurityUtils.getSubject(); subject.logout(); return "login"; } } ``` 测试三个页面访问 15、编写首页(index.html)、登录页(login.html) ```html Title

index

退出
main
manager
administrator
``` ```html Title
用户名:
密码:
``` 访问权限如下: 1、必须登录才能访问 main.html 2、当前用户必须拥有 manage 授权才能访问 manage.html 3、当前用户必须拥有 administrator 角色才能访问 administrator.html 至此,整合完成!!! > 认证过滤器 anon:无需认证。 authc:必须认证。 authcBasic:需要通过 HTTPBasic 认证。 user:不一定通过认证,只要曾经被 Shiro 记录即可,比如:记住我。 > 授权过滤器 perms:必须拥有某个权限才能访问。 role:必须拥有某个角色才能访问。 port:请求的端口必须是指定值才可以。 rest:请求必须基于 RESTful,POST、PUT、GET、DELETE。 ssl:必须是安全的 URL 请求,协议 HTTPS。 # 3、SpringBoot 整合 Shiro+Jwt(前后端分离)