Shiro实现(一): SSM整合笔记实现登录,授权功能

开篇

  1. 本项目已经上传)github,建议对照代码理解
  2. 本篇主要讲Shiro框架与SSM框架结合,实现登录和授权功能
  3. 利用spring 的aop切面思想,很简单得融合Shiro权限框架
  4. 代码

需要明白两个点:

  1. 通过Subject.login() 登录成功后,用户信息就会保存在安全管理器上,也就是 SecurityManager。就可以在程序任何地方获取到该用户对象。
  2. 在重写拦截器两个方法是重点,在登录的时候就需要把授权信息也存到安全管理器上,所以登录成功后,所有判断权限都不需要在业务逻辑上做判断,shiro框架已经帮你拦截并判断好。

1. jar包

shiro有很多种类型的包,用途有web的,非web的等,*-all 代表所有都在里面
shiro-all.jar

2.配置

其实shiro权限控制就是通过拦截器来进行判断用户权限的,因此shiro拦截器的配置跟springMVC的拦截器配置是类似的。

第一步

尽然是通过aop来使用shiro,那就需要在web.xml里添加一个shiro的拦截器。

ps:这个shiro拦截器是如何加载的呢? 因为这个项目shiro与spring整合了,所有运行项目的时候,spring监听器会去寻找并加载shiro拦截器

<!-- Spring监听器 -->
 <listener>
   <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
 </listener>

shiro拦截器配置如下:

<!-- 配置由Spring提供的过滤器,用于整合shiro框架 -->
  <!-- 在项目启动的过程中,当前过滤器会从Spring工厂中提取同名对象 -->
  <filter>
    <filter-name>shiroFilter</filter-name>
    <filter-class>
      org.springframework.web.filter.DelegatingFilterProxy
    </filter-class>
  </filter>
  <filter-mapping>
    <filter-name>shiroFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

第二步

类似springmvc一样,需要写一个配置文件来配置拦截器。
在shiro配置文件中,有两种类型。
- ini配置文件 (很多博客教程上都是使用这种配置方式。)
- xml配置文件 (这次项目使用这种配置方式)

首先创建shiro-context.xml

ps: 很简单,就相当于spring的配置文件一样,因为shiro是跟spring很好结合的。
下面会慢慢解释这个配置文件

<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager" /> <!--加载管理器-->
        <property name="loginUrl" value="/user/login" />    <!--没有登录的时候,跳转到这个页面-->
        <property name="unauthorizedUrl" value="/user/nopermission" /> <!--当没有权限的时候,跳转到这个url-->
         
        <property name="filterChainDefinitions">
            <value>
                /user/login = anon <!--可以不需要登录-->
                /user/readName = authc, perms[/readName]  <!-- perms 表示需要该权限才能访问的页面 -->
                /user/readData = authc, perms[/readData]
                /user/* = authc <!-- authc 表示需要认证才能访问的页面 -->
            </value>
        </property>
    </bean>

    <!-- 自定义Realm -->
    <bean id="myShiroRealm" class="com.Shiro.MyShiroReaml">
        <!-- businessManager 用来实现用户名密码的查询 -->
        <property name="shiroService" ref="accountService" />
    </bean>

    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <!-- 注入realm -->
        <property name="realm" ref="myShiroRealm"/>
    </bean>

    <!--声明一个Service 注入到自定义Realm-->
    <bean id="accountService" class="com.Service.Impl.ShiroServiceImpl"/>
    <!-- <bean id="shiroCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> 
        <property name="cacheManager" ref="cacheManager" /> </bean> -->
</beans>
  1. 拦截器需要加载一个安全管理器,SecurityManager 是整个shiro框架的核心
    <property name="securityManager" ref="securityManager" /> <!--加载管理器-->
  1. 拦截url,这里就是配置拦截url。anon.authc.这些名词其实就是shiro已经写好的拦截器,只需要调用可以了。在如果权限不够,则会跳转到指定的url
  <property name="filterChainDefinitions">
            <value>
                /user/login = anon <!--可以不需要登录-->
                /user/readName = authc, perms[/readName]  <!-- perms 表示需要该权限才能访问的页面 -->
                /user/readData = authc, perms[/readData]
                /user/* = authc <!-- authc 表示需要认证才能访问的页面 -->
            </value>
        </property>

过滤器列表:

208783.jpg

第三步 拦截后,对用户进行验证

当你拦截器设置好了,可以成功拦截用户的操作,然后我们需要对用户进行权限验证。所以我们需要继承shiro的AuthorizingRealm拦截器,重写两个方法。
- 重写doGetAuthenticationInfo方法是:登录验证,当需要登录的时候,就会调用该方法进行验证。
- 重写doGetAuthorizationInfo方法:这个是授权验证,与上面的过滤器相结合。

思路如下:

  • 登录验证: 根据账号从数据库获取账号密码进行比较,如果一致则登录成功,就会保存到,否则登录失败
  • 授权验证:在登录成功后,根据用户id获取到该用户的权限,并把权限保存在安全管理器之中,当用户访问的时候,会从管理器中判断该用户是否有权限去访问该url。
    代码如下:
public class MyShiroReaml extends AuthorizingRealm {
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) {

        /**
         * 
         * 流程
         * 1.根据用户user->2.获取角色id->3.根据角色id获取权限permission
         */
        //方法一:获得user对象
        User user=(User)pc.getPrimaryPrincipal();
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //获取permission
        if(user!=null) {
            List<Permission> permissionsByUser = shiroService.getPermissionsByUser(user);
            if (permissionsByUser.size()!=0) {
                for (Permission p: permissionsByUser) {

                    info.addStringPermission(p.getUrl());
                }
                return info;
            }
        }

        //方法二: 从subject管理器里获取user
//      Subject subject = SecurityUtils.getSubject();
//      User _user = (User) subject.getPrincipal();
//      System.out.println("subject"+_user.getUsername());




        return null;
    }

    // 认证方法
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("进来验证了");
        //验证账号密码
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        System.out.println("1:"+token.getUsername());
        User user = shiroService.getUserByUserName(token.getUsername());
        System.out.println("2");
        if(user==null){
            return null;
        }
        //最后的比对需要交给安全管理器
        //三个参数进行初步的简单认证信息对象的包装
        AuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), this.getClass().getSimpleName());

        return info;
    }


    private ShiroService shiroService;

    public ShiroService getShiroService() {
        return shiroService;
    }

    public void setShiroService(ShiroService shiroService) {
        this.shiroService = shiroService;
    }
}

第四步

shrio的基本配置已经完成了,接下来是基础代码块了, 也许还有很多疑问,没关系。下载代码下来看看,就明白了

Controller层

1.登录代码块

 @RequestMapping(value = "/login")
    public String Login(String username, String password, HttpSession session, Model model){
        if(username==null){
            model.addAttribute("message", "账号不为空");
            return "login";
        }
        //主体,当前状态为没有认证的状态“未认证”
        Subject subject = SecurityUtils.getSubject();
        // 登录后存放进shiro token
        UsernamePasswordToken token=new UsernamePasswordToken(username,password);
        User user;
        //登录方法(认证是否通过)
        //使用subject调用securityManager,安全管理器调用Realm
        try {
            //利用异常操作
            //需要开始调用到Realm中
            System.out.println("========================================");
            System.out.println("1、进入认证方法");
            subject.login(token);
            user = (User)subject.getPrincipal();
            session.setAttribute("user",subject);
            model.addAttribute("message", "登录完成");
            System.out.println("登录完成");
        } catch (UnknownAccountException e) {
            model.addAttribute("message", "账号密码不正确");
            return "index";
        }
        return "test";
    }

  1. 权限代码块
shiro已经帮我们验证了,所以我们只需要写基本业务逻辑就可以,不需要再写权限验证代码了
 @RequestMapping("/readName")
    public String readName(HttpSession session){

        return "name";
    }

    @RequestMapping("/readData")
    public String readData(){

        return "data";
    }


    @RequestMapping("/nopermission")
    public String noPermission(){
        return "error";
    }

Service 层

public class ShiroServiceImpl implements ShiroService {

    @Autowired
    private ShiroDao shiroDao;

    public User getUserByUserName(String username) {
        //根据账号获取账号密码
        User userByUserName = shiroDao.getUserByUserName(username);
        return userByUserName;
    }

    public List<Permission> getPermissionsByUser(User user) {
        //获取到用户角色userRole
        List<Integer> roleId = shiroDao.getUserRoleByUserId(user.getId());
        List<Permission> perArrary = new ArrayList<>();

        if (roleId!=null&&roleId.size()!=0) {
            //根据roleid获取peimission
            for (Integer i : roleId) {
                perArrary.addAll(shiroDao.getPermissionsByRoleId(i));
            }
        }

        System.out.println(perArrary);
        return perArrary;
    }


}

POJO层

public class Permission {
    private int id;
    private String token;
    /**资源url**/
    private String url;
    /**权限说明**/
    private String description;
    /**所属角色编号**/
    private int roleId;
    }

public class Role {
    private int id;
    /**角色**/
    private String role;
    /**说明**/
    private String description;
    }

public class User {
    private int id;
    private String account;
    private String password;
}

第5步 数据库表的建立

数据表已经附加到代码上传到github
数据库中添加一条用户、角色、以及权限数据,并且在关联表中添加一条关联数据

  • Permission 权限表
Permission.png
  • 角色管理表
角色管理表.png
  • 用户表
用户表.png
  • 用户角色对照表
用户角色对照表.png

总结

  1. Shiro框架 与spring结合,对原有的代码块改变很少。完美得实现了AOP思想
  2. Shiro的功能很强大,该项目实现了登录已经授权的基本功能,接下来还会有单点登录,缓存机制,密码加密等功能的演示。
  3. 代码已放到github上,可以clone下来细看。
  4. 代码
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,587评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,724评论 6 342
  • #白马声慢,我自手书# 有的人真的是相见恨晚 有的人真的是上天注定会遇到 那天无意和舍友聊起前男友,开玩笑让她给我...
    哎呦喂_dc07阅读 272评论 0 0
  • 不学狂驰子,直在百年中。 这是陶渊明对田畴的评价,一个身处乱世,却在这浊世中保持了个人的高风亮节。 子泰徐无山,...
    9ml叶酸阅读 651评论 0 2
  • 之前好好的,突然报这个错误,而且一次成功、一次失败!! 网上查了半天 没有好的解决办法,继续查,先mark下。 问...
    三十一_iOS阅读 8,014评论 7 2