如何使用Shiro和Redis优雅的搭建一个简单的角色验证框架

前言

相信很多Shiro新手都和我一样,刚上手Shiro时候一脸懵。Realm不是个权限库吗?怎么会做认证和校验的活儿?为什么在Shiro的配置文件里又见到它了?Redis怎么集成到Shiro?Session储存的值怎么交给Redis管理?且看我用一个简单的小demo一一道来。

demo要求

1、角色共有三个:教师、学生、管理员。
2、教师和学生分别使用工号和学号登录,管理员算在教师内。
3、登录时完成身份验证和权限验证。
4、认证信息和权限信息交给Redis存储。
5、Session内存储的信息交给Redis存储。

第一步:设计角色与权限

根据要求,我们需要建两张表:学生表和教师表,在教师表中设计一个字段用来区分教师和管理员。
角色设计思路:工号和学号不会重复,所以可以使用工号和学号在数据库中查询老师和学生。从老师表中查出的赋予老师角色,从学生表中查出的赋予学生角色。

1、教师表:

CREATE TABLE `teacher`  (
  `t_id` int NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `job_id` varchar(32) NULL DEFAULT '' COMMENT '工号',
  `password` varchar(32) NULL COMMENT '密码',
  `status` tinyint NULL COMMENT '状态码,0为教师,1为管理员',
  PRIMARY KEY (`t_id`)
);

在这里插入图片描述
2、学生表:

CREATE TABLE `spring`.`Untitled`  (
  `s_id` int NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `stu_id` varchar(32) NULL COMMENT '学号',
  `password` varchar(32) NULL COMMENT '密码',
  PRIMARY KEY (`s_id`)
);

在这里插入图片描述
现在开始我们来配置Shiro

第二步:引入依赖、配置Realm

引入Shiro依赖:

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring-boot-web-starter</artifactId>
            <version>1.10.0</version>
        </dependency>

我们来看一下登录鉴权的流程
在这里插入图片描述
也就是说subject在登陆时,底层是到Realm进行认证和授权。那么我们就可以新建一个Realm类,继承AuthorizingRealm类,重写这两个方法,定义我们自己的认证和授权方式。

@Component
public class DefinitionRealm extends AuthorizingRealm {
    @Autowired
    private StudentService studentService;
    @Autowired
    private TeacherService teacherService;

    /**
     * 授权方法
     * */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("鉴权发生");
        //获得主身份信息
        String principal = (String) principalCollection.getPrimaryPrincipal();
        //根据用户名获取当前用户的角色信息、权限信息
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        //获取角色
        //如果角色为学生,给角色赋予学生权限
        Student studentByStuId = studentService.getStudentByStuId(principal);
        if(studentByStuId!=null){
            simpleAuthorizationInfo.addRole("student");
        }
        //如果角色为教师,给角色赋予教师权限
        Teacher teacherByJobId = teacherService.getTeacherByJobId(principal);
        if(teacherByJobId!=null){
            if(teacherByJobId.getStatus()==1){
                simpleAuthorizationInfo.addRole("admin");
            }else {
                simpleAuthorizationInfo.addRole("teacher");
            }
        }

        //返回角色权限
        return simpleAuthorizationInfo;
    }

    /**
     * 认证方法
     * */
    @Override
    protected  AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authToken){
        //获得用户名
        String principal = (String) authToken.getPrincipal();
        //获取学生身份信息
        Student student = studentService.getStudentByStuId(principal);
        if(student!=null){
            return new SimpleAuthenticationInfo(
                    authToken.getPrincipal(),
                    student.getPassword(),
                    principal
            );
        }
        //获取教师身份信息
        Teacher teacher = teacherService.getTeacherByJobId(principal);
        if(teacher!=null){
            return new SimpleAuthenticationInfo(
                    authToken.getPrincipal(),
                    teacher.getPassword(),
                    principal
            );
        }
        return null;
    }
}

如此一来,我们就实现了认证和授权的方法。但是我们还需要将Realm放入SecurityManager中进行管理。

第三步:创建Shiro配置文件,并将realm交给securityManager管理


@Configuration
public class ShiroConfiguration {

    @Autowired
    private DefinitionRealm realm;

	//过滤器,设置访问任何url不需要权限
	@Bean 
    public DefaultShiroFilterChainDefinition shiroFilterChainDefinition(){
        DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();

        definition.addPathDefinition("/**" , "anon");
        return definition;
    }

    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(){
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();

        //给安全管理器设置realm
        defaultWebSecurityManager.setRealm(realm);
        return defaultWebSecurityManager;
    }
}

至此,我们的框架算是简单搭建好了,写个controller测试一下

@Controller
public class testController {
    @GetMapping("/login")
    @ResponseBody
    public boolean login(@RequestParam String username,
                        @RequestParam String password){
        AuthenticationToken token = new UsernamePasswordToken(username , password);
        Subject subject = SecurityUtils.getSubject();
        subject.login(token);
        return subject.hasRole("student");
    }
}

测试结果:
在这里插入图片描述
登录认证和校验完成后我们现在来配置Redis

第四步:配置Redis

引入依赖:

<dependency>
            <groupId>org.crazycake</groupId>
            <artifactId>shiro-redis</artifactId>
            <version>2.4.2.1-RELEASE</version>
            <exclusions> <!--要排除shiro-core,不然会有版本冲突-->
                <exclusion>
                    <groupId>org.apache.shiro</groupId>
                    <artifactId>shiro-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

在yml文件中将session类型改为Redis

  redis: # session设置redis 
  session:
    store-type: redis
    timeout: 30m

然后我们现在来配置redis作为Shiro的缓存
配置Redis的存取方法:


public class RedisCache<K , V> implements Cache<K , V> {

    private String cacheName;

    public RedisCache(){}

    public RedisCache(String cacheName){
        this.cacheName = cacheName;
    }


    @Override
    public V get(K k) throws CacheException {

        return (V) getRedisTemplate().opsForHash().get(this.cacheName,k.toString());
    }

    @Override
    public V put(K k, V v) throws CacheException {
        getRedisTemplate().opsForHash().put(this.cacheName , k.toString() , v);
        return null;
    }

    @Override
    public V remove(K k) throws CacheException {

        return (V) getRedisTemplate().opsForHash().delete(this.cacheName , k.toString());
    }

    @Override
    public void clear() throws CacheException {
        getRedisTemplate().delete(this.cacheName);
    }

    @Override
    public int size() {
        return getRedisTemplate().opsForHash().size(this.cacheName).intValue();
    }

    @Override
    public Set<K> keys() {
        return getRedisTemplate().opsForHash().keys(this.cacheName);
    }

    @Override
    public Collection<V> values() {
        return getRedisTemplate().opsForHash().values(this.cacheName);
    }

    private RedisTemplate getRedisTemplate(){
        RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtil.getBean("redisTemplate");
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        return redisTemplate;
    }
}

这里是配置一些Redis作为缓存的CRUD方法。
配置RedisCacheManeger:

public class RedisCacheManeger implements CacheManager {
    @Override
    public <K, V> Cache<K, V> getCache(String cacheName) throws CacheException {
        return new RedisCache<K , V>(cacheName);
    }
}

在Shiro配置文件中设置缓存管理器

@Configuration
public class ShiroConfiguration {

    @Autowired
    private DefinitionRealm realm;

	//过滤器,设置访问任何url不需要权限
	@Bean 
    public DefaultShiroFilterChainDefinition shiroFilterChainDefinition(){
        DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();

        definition.addPathDefinition("/**" , "anon");
        return definition;
    }

    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(){
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
		
		//设置缓存管理器
            realm.setCacheManager(new RedisCacheManeger());
            //开启全局缓存
            realm.setCachingEnabled(true);
            //开启认证缓存,指定缓存名称
            realm.setAuthenticationCachingEnabled(true);
            realm.setAuthenticationCacheName("authenticationCache");
            //开启授权缓存,指定缓存名称
            realm.setAuthorizationCachingEnabled(true);
            realm.setAuthorizationCacheName("authorizationCache");
        //给安全管理器设置realm
        defaultWebSecurityManager.setRealm(realm);
        return defaultWebSecurityManager;
    }
}

现在我们的session和缓存管理器都已经设置好,现在测试一下运行的情况。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
现在我们的认证信息、权限信息、session都完美的存储到了数据库里,至此,小demo算是编写完成。

在这篇博客里,关于Shiro的很多知识我没有提到,比如过滤器的设置、ini文件配置。本篇博客在于教会新手如何快速上手Shiro,如果本篇博客的内容不足以支撑读者完成项目的话,可以搜一些其他的优质文章进行深入学习。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值