01. shiro框架

1. Shiro的核心组件

  1. UsernamePasswordToken:用来封装用户的登录信息,通过登录信息创建令牌Token;登陆的过程即Shiro验证令牌是否具有合法身份以及相关权限
  2. SecurityManager:Shiro的核心,负责安全认证和授权
  3. Subject:Shiro的一个抽象概念,包含了用户信息
  4. Realm开发者自定义的模块,根据项目的需求,验证和授权的逻辑在Realm中实现
  5. AuthenticationInfo:用户的角色信息集合,认证的时候使用
  6. AuthorizationInfo:角色的权限信息集合,授权时使用
  7. DefaultWebSecurityManager:安全管理器,用户自定义的Realm需要注入安全管理器才能使用
  8. ShiroFilterFactoryBean:过滤器工厂,Shiro的基本运行机制是开发者定义规则,shiro去执行;具体的执行操作就是有ShiroFilterFactory创建一个个Filter对象来完成

在这里插入图片描述

2. 使用步骤

2.1 引入依赖

<!-- Shiro整合Spring -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.5.3</version>
</dependency>

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.20</version>
</dependency>

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.3.1.tmp</version>
</dependency>

2.2 创建数据表 account,添加两条记录

CREATE TABLE `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=4 DEFAULT CHARSET=utf8;

LOCK TABLES `account` WRITE;
INSERT INTO `account` VALUES (1,'zs','123123','',''),(2,'ls','123123','manage',''),(3,'ww','123123','manage','administrator');
UNLOCK TABLES;

2.3 Java代码准备

创建实体类Account

@Data
public class Account {
    private Integer id;
    private String username;
    private String password;
    private String perms;
    private String role;
}

创建AccountMapper接口

public interface AccountMapper extends BaseMapper<Account> {
}

创建application.yml

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Drive
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

启动类添加注解扫描Mapper接口

@SpringBootApplication
@MapperScan("com.southwind.springbootshirodemo.mapper")
public class SpringbootshirodemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootshirodemoApplication.class, args);
    }

}

首先通过单元测试调试 AccoutMapper 接口,能够调通之后,编写service层代码

@SpringBootTest
class AccountMapperTest {

    @Autowired
    private AccountMapper accountMapper;

    @Test
    void test(){
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("username","user");
        Account account = accountMapper.selectOne(wrapper);
        System.out.println(account);
    }
}

service层代码

public interface AccountService {
    public Account findByUsername(String username);
}
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountMapper accountMapper;

    @Override
    public Account findByUsername(String username) {
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("username",username);
        return accountMapper.selectOne(wrapper);
    }
}

2.4 配置shiro,重点开始了!!!

回到shiro,完成自己的realm的编写

public class MyRealm extends AuthorizingRealm {

    @Autowired
    private AccountService accountService;

    /**
     * 授权
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

    /**
     * 认证
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        // 1. 获取令牌
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        // 2. 从令牌中获取用户信息
        Account account = accountService.findByUsername(token.getUsername());
        // 3. 返回用户角色信息集合
        if(account != null){
            return new SimpleAuthenticationInfo(account,account.getPassword(),getName());
        }
        return null;
    }
}
// 先根据 username 进行查询,如果返回 null,则表示用户名错误,直接 return null 即可,Shiro 会自动抛出 UnknownAccountException 异常。
// 如果返回不为 null,则表示用户名正确,再验证密码,直接返回  SimpleAuthenticationInfo 对象即可,这里面封装了用户的角色信息集合

自定义MyRealm后,需要配置到安全管理器和shiro的过滤器工厂中

Realm -》 DefaultWebSecurityManager -》 ShiroFilterFactory

@Configuration
public class ShiroConfig {

    @Bean
    public MyRealm myRealm(){
        return new MyRealm();
    }
    
    @Bean
    public DefaultWebSecurityManager manager(@Qualifier("myRealm") MyRealm myRealm){
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        manager.setRealm(myRealm);
        return manager;
    }
    
    @Bean
    public ShiroFilterFactoryBean filterFactoryBean(@Qualifier("manager") DefaultWebSecurityManager manager){
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        factoryBean.setSecurityManager(manager);
        return factoryBean;
    }
}

ShiroFilterFactory是Shiro自带的一个Filter工厂实例,所有的认证和授权的操作都是在这里完成的,开发者只需要定义规则,具体执行由Shiro完成

给ShiroFilterFactory设置规则

认证过滤器:
anon:无需认证即可访问,游客身份。
authc:必须认证(登录)才能访问。
authcBasic:需要通过 httpBasic 认证。
user:不一定已通过认证,只要是曾经被 Shiro 记住过登录状态的用户就可以正常发起请求,比如 rememberMe。

授权过滤器:
perms:必须拥有对某个资源的访问权限(授权)才能访问。
role:必须拥有某个角色权限才能访问。
port:请求的端口必须为指定值才可以访问。
rest:请求必须是 RESTful,method 为 post、get、delete、put。
ssl:必须是安全的 URL 请求,协议为 HTTPS。

比如,我们创建三个页面,main.html、manage.html、administrator.html,要求如下:

1、必须是登录状态才可以访问 main.html。

2、用户必须拥有 manage 授权才可以访问 manage.html。

3、用户必须拥有 administrator 角色才能访问 administrator.html。

那么我们可以给ShiroFilterManager配置成为下面的方式

@Bean
public ShiroFilterFactoryBean filterFactoryBean(@Qualifier("manager") DefaultWebSecurityManager manager){
    ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
    factoryBean.setSecurityManager(manager);
    Map<String,String> map = new HashMap<>();
    map.put("/main","authc");
    map.put("/manage","perms[manage]");
    map.put("/administrator","roles[administrator]");
    factoryBean.setFilterChainDefinitionMap(map);
    //设置登录页面
    factoryBean.setLoginUrl("/login");
    //未授权页面
    factoryBean.setUnauthorizedUrl("/unauth");
    return factoryBean;
}

对应的Controller如图所示

@Controlle
public class MyController {

    @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);
        try {
            subject.login(token);
            return "index";
        } catch (UnknownAccountException e) {
            model.addAttribute("msg","用户名错误");
            return "login";
        } catch (IncorrectCredentialsException e) {
            model.addAttribute("msg", "密码错误");
            return "login";
        }
    }

    @RequestMapping("/unauth")
    @ResponseBody
    public String unauth(){
        return "未授权没有访问权限";
    }
}

2.5 那么现在只需要登录就可以访问main.html

但是无法访问manager.html,这是因为没有授权,接下来我们完成授权的操作,回到MyRealm

修改doGetAuthorizationInfo方法

@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    //获取当前登录对象
    Subject subject = SecurityUtils.getSubject();
    Account account = (Account) subject.getPrincipal();

    //设置角色
    Set<String> roles = new HashSet<>();
    roles.add(account.getRole());
    SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);

    //设置权限
    info.addStringPermission(account.getPerms());
    return info;
}

数据库数据如图所示

在这里插入图片描述

zs 没有权限和角色,所以登录之后只能访问 main.html。

ls 拥有 manage 权限,没有角色,所以登录之后可以访问 main.html、manage.html。

ww 拥有 manage 权限和 administrator 角色,所以登录之后可以访问 main.html、manage.html、administrator.html。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SLF4J: No SLF4J providers were found. SLF4J: Defaulting to no-operation (NOP) logger implementation SLF4J: See https://www.slf4j.org/codes.html#noProviders for further details. Exception in thread "main" org.apache.shiro.config.ConfigurationException: Unable to instantiate class [org.apache.shiro.web.mgt.DefaultWebSecurityManager] for object named 'securityManager'. Please ensure you've specified the fully qualified class name correctly. at org.apache.shiro.config.ReflectionBuilder.createNewInstance(ReflectionBuilder.java:309) at org.apache.shiro.config.ReflectionBuilder$InstantiationStatement.doExecute(ReflectionBuilder.java:927) at org.apache.shiro.config.ReflectionBuilder$Statement.execute(ReflectionBuilder.java:887) at org.apache.shiro.config.ReflectionBuilder$BeanConfigurationProcessor.execute(ReflectionBuilder.java:765) at org.apache.shiro.config.ReflectionBuilder.buildObjects(ReflectionBuilder.java:260) at org.apache.shiro.config.IniSecurityManagerFactory.buildInstances(IniSecurityManagerFactory.java:167) at org.apache.shiro.config.IniSecurityManagerFactory.createSecurityManager(IniSecurityManagerFactory.java:130) at org.apache.shiro.config.IniSecurityManagerFactory.createSecurityManager(IniSecurityManagerFactory.java:108) at org.apache.shiro.config.IniSecurityManagerFactory.createInstance(IniSecurityManagerFactory.java:94) at org.apache.shiro.config.IniSecurityManagerFactory.createInstance(IniSecurityManagerFactory.java:46) at org.apache.shiro.config.IniFactorySupport.createInstance(IniFactorySupport.java:123) at org.apache.shiro.util.AbstractFactory.getInstance(AbstractFactory.java:47) at com.xiu.Quickstart.main(Quickstart.java:26) Caused by: org.apache.shiro.util.UnknownClassException: Unable to load class named [org.apache.shiro.web.mgt.DefaultWebSecurityManager] from the thread context, current, or system/application ClassLoaders. All heuristics have been exhausted. Class could not be found. at org.apache.shiro.util.ClassUtils.forName(ClassUtils.java:152) at org.apache.shiro.util.ClassUtils.newInstance(ClassUtils.java:168) at org.apache.shiro.config.ReflectionBuilder.createNewInstance(ReflectionBuilder.java:302) ... 12 more
06-10
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值