Spring Boot整合shiro

1、shiro是什么

用官方的话说Apache Shiro是一个功能强大且易于使用的Java安全框架,它为开发人员提供了一种直观而全面的解决方案,用于身份验证,授权,加密和会话管理。
首先我们先看官方给出的Shiro外部结构图,看shiro是如何完成工作的:


官方结构图.png

shiro主要有三大功能模块:

Subject:

应用代码直接交互的对象Subject,也就是说Shiro的对外API核心就是Subject,代表了当前的用户,也不一定是一个具体的人,也当前应用交互的任何东西都是Subject,与Subject的所有交互都会委托给SecurityManager,Subject其实就是一个门面,SecurityManager才是实际的执行者

SecurityManager:

安全管理器,即所有与安全有关的操作都会与SecurityManager交互,并且它管理着所有的Sbject,它才是Shiro的核心,负责与Shiro的其他组件进行交互

Realms:

Shiro从Realm获取安全数据(用户,角色,权限),就是说SecurityManager要验证用户身份,它就需要从Realm获取相应的用户进行比较,来确定用户的身份是否合法,也需要用Realm得到用户相应的角色、权限,进行验证用户的操作是否能够进行

2、集成Shiro

1、导入jar包

在pom文件中添加shiro依赖

 <!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.6.0</version>
        </dependency>
2、编写配置类

创建ShiroConfig类

@Configuration
public class ShiroConfig {
    //ShiroFilterFactoryBean
    //DefaultWebSecurityManager
    //创建realm对象
}

这三个就对应我们刚刚说的三个模块,然后就开始配置这三个对象,我们先从realm开始配置,realm对象需要我们自己先定义,我们先新建一个ShiroRealm类然后继承 AuthorizingRealm,然后重写他的方法,一个授权,一个认证

//自定义realm 需要继承 AuthorizingRealm
public class ShiroRealm extends AuthorizingRealm {
    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }
    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        return null;
    }
}

然后返回ShiroConfig去创建realm对象

@Configuration
public class ShiroConfig {
    //ShiroFilterFactoryBean
    //DefaultWebSecurityManager

    //创建realm对象
    @Bean
    public ShiroRealm shiroRealm(){
        return new ShiroRealm();
    }
}

这样我们写的对象就被spring托管了,接下来我们写DefaultWebSecurityManager,因为DefaultWebSecurityManager需要Realm

@Configuration
public class ShiroConfig {
    //ShiroFilterFactoryBean
    
    //DefaultWebSecurityManager
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("shiroRealm") ShiroRealm shiroRealm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //关联realm
        securityManager.setRealm(shiroRealm);
        return securityManager;
    }

    //创建realm对象
    @Bean
    public ShiroRealm shiroRealm(){
        return new ShiroRealm();
    }
}

接下来写ShiroFilterFactoryBean

@Configuration
public class ShiroConfig {
    //ShiroFilterFactoryBean
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager manager){
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        //设置安全管理器
        bean.setSecurityManager(manager);
        return bean;
    }
    //DefaultWebSecurityManager
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("shiroRealm") ShiroRealm shiroRealm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //关联realm
        securityManager.setRealm(shiroRealm);
        return securityManager;
    }

    //创建realm对象
    @Bean
    public ShiroRealm shiroRealm(){
        return new ShiroRealm();
    }
}

3、设置过滤器

前面我们已经把shiro初步集成了,接下来就需要配置过滤器

//ShiroFilterFactoryBean
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager manager){
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        //设置安全管理器
        bean.setSecurityManager(manager);
        /*
            添加shiro的内置过滤器
            anon:  无需认证就可访问
            authc: 必须认证才能访问
            user:  必须拥有 记住我功能才可访问
            perms: 拥有对某个资源的权限才能访问
            role:  拥有某个角色权限才能访问
         */
        Map<String,String>filterMap = new HashMap<>();
        filterMap.put("/version/*","authc");
        filterMap.put("/api/*","anon");
        bean.setFilterChainDefinitionMap(filterMap);
        //设置登录请求
        bean.setLoginUrl("/home/toLogin");
        return bean;
    }

这样配置的意思是路径/version/是需要认证才可访问,/api/无需认证就可访问,然后没认证的返回登录页面去认证,接下来试试,在网页中输入网址


image.png

因为没认证过直接跳转到了登录页面,这样就算成功了


image.png

4、实现登录功能

现在我们已经成功拦截了我们设置的路径,然后就要实现登录通过认证,首先写一个用户实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Admin {
    private Integer id;
    //用户名称
    private String name;
    //登录帐号
    private String username;
    //登录密码
    private String password;
    //创建时间
    private Date createTime;
}

然后写登录方法

    @PostMapping("login")
    public String login(String adminName, String password, Model model) {
        Admin admin = adminMapper.searchByAccount(adminName);
        if (admin == null){
            model.addAttribute("error","帐号密码错误");
            return "home/login";
        }
        if (!admin.getPassword().equals(password)){
            model.addAttribute("error","帐号密码错误");
            return "home/login";
        }
        //获取当前用户
        Subject subject = SecurityUtils.getSubject();
        //封装用户的登录数据
        UsernamePasswordToken token = new UsernamePasswordToken(admin.getUsername(),admin.getPassword());
        try {
            subject.login(token);  //执行登录方法
            return "redirect:/version/list";
        } catch (Exception e){ 
            e.printStackTrace();
        }
        return "home/login";
    }

这样我们在请求登录请求后他会去执行doGetAuthenticationInfo方法做认证操作,那我们就去里面写认证操作

 //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        String adminName = token.getUsername();
        if (adminName != null && !adminName.equals("")){
            Admin admin = adminMapper.searchByAccount(adminName);
            if (admin != null){
                return new SimpleAuthenticationInfo(admin.getUsername(),admin.getPassword(),getName());
            }
        }
        return null;
    }

如果查询数据库没有该用户return null后会抛出一个UnknownAccountException异常,成功返回AuthenticationInfo的实现类
这样我们就实现了登录认证功能

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