Shiro安全框架

www.myblog.cn/shiro
Shiro安全框架的讲解和使用!ShiroApache下的一个开源项目,我们称之为Apache Shiro。它是一个很易用与Java项目的的安全框架,提供了认证、授权、加密、会话管理,与spring Security 一样都是做一个权限的安全框架,但是与Spring Security 相比,在于 Shiro 使用了比较简单易懂易于使用的授权方式。shiro属于轻量级框架,相对于security简单的多,也没有security那么复杂。

开篇:Shiro安全框架的介绍

img

Authentication:身份认证 / 登录,验证用户是不是拥有相应的身份;

Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;

Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通 JavaSE 环境的,也可以是如 Web 环境的;

Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;

Web Support:Web 支持,可以非常容易的集成到 Web 环境;

Caching:缓存,比如用户登录后,其用户信息、拥有的角色 / 权限不必每次去查,这样可以提高效率;

Concurrency:shiro 支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;

Testing:提供测试支持;

Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;

Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。

记住一点,Shiro 不会去维护用户、维护权限;这些需要我们自己去设计 / 提供;然后通过相应的接口注入给 Shiro 即可。

从外部来看 Shiro ,如下图。

img

Subject:主体,代表了当前 “用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是 Subject,如网络爬虫,机器人等;即一个抽象概念;所有 Subject 都绑定到 SecurityManager,与 Subject 的所有交互都会委托给 SecurityManager;可以把 Subject 认为是一个门面;SecurityManager 才是实际的执行者;

SecurityManager:安全管理器;即所有与安全有关的操作都会与 SecurityManager 交互;且它管理着所有 Subject;可以看出它是 Shiro 的核心,它负责与后边介绍的其他组件进行交互,如果学习过 SpringMVC,你可以把它看成 DispatcherServlet 前端控制器;

Realm:域,Shiro 从从 Realm 获取安全数据(如用户、角色、权限),就是说 SecurityManager 要验证用户身份,那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从 Realm 得到用户相应的角色 / 权限进行验证用户是否能进行操作;可以把 Realm 看成 DataSource,即安全数据源。

一,Shiro认证

首先新建一个springboot项目,在maven中加入shiro-core核心包和junit测试包。

 		<!--加入shiro核心包-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.2.3</version>
        </dependency>
        <!--junit单元测试-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>

然后新建一个类,进行Shiro认证的测试。

步骤说明

1,构建securityManager环境

2,创建Realm,设置账号和密码

3,把设置好的Realm添加到securityManager环境

4,主体提交认证请求

5,创建UsernamePasswordToken,相当于登录操作,获取登录时的账号和密码

6,subject.isAuthenticated()判断账号和密码是否正确

public class ShiroController {
	//创建Realm是对用户的账号和密码进行管理的
    SimpleAccountRealm simpleAccountRealm=new SimpleAccountRealm();

    @Before //都是junit的注解,目的是在单元测试中可以调用本方法
    public void addUser(){
        //这里设置好用户名和密码
        simpleAccountRealm.addAccount("admin","123");
    }

    @Test
    public void testAuthentication(){
        //1,构建securityManager环境
        DefaultSecurityManager defaultSecurityManager=new DefaultSecurityManager();
        //把上面的用户名密码设置添加到securityManager环境
        defaultSecurityManager.setRealm(simpleAccountRealm);

        //2,主体提交认证请求
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        Subject subject = SecurityUtils.getSubject();

        //这里是登录环境,获取到账号和密码,shiro会自动识别是否正确
        UsernamePasswordToken token=new UsernamePasswordToken("admin","123");
        //测试登录是否可以成功
        subject.login(token);
        boolean authenticated = subject.isAuthenticated();//true验证通过,false账号密码不正确
        System.err.println("是否可以登录"+authenticated);  //true

        //shiro的登出方法
        subject.logout();
        boolean authenticated1 = subject.isAuthenticated();
        System.out.println("已经退出了,状态为false ########"+authenticated1); //false
    }
}

二,Shiro授权

simpleAccountRealm.addAccount的操作里可以添加对应的权限

比如说,给admin账户添加一个admin权限(可以设置多个权限)

 SimpleAccountRealm simpleAccountRealm=new SimpleAccountRealm();

    @Before //都是junit的注解,目的是在单元测试中可以调用本方法
    public void addUser(){
        //这里设置好用户名和密码
        simpleAccountRealm.addAccount("admin","123","admin");
    }

然后再下面登录验证通过之后,再验证是否具有该权限

 subject.checkRoles("admin");

如果该账户有admin权限则顺利通过,没有则会报错。

在这里插入图片描述

三,Shiro自定义Realm

在说自定义Realm之前,先说一下Shiro的内置Realm

  • IniRealm
  • JdbcRealm

1,IniRealm的使用

用户是由IniRealm控制的。

创建一个IniRealm,指定账号密码的文件(resources文件夹下的user.ini文件)

    IniRealm iniRealm=new IniRealm("classpath:user.ini");

然后把该Realm添加到securityManager环境

	//把上面的用户名密码设置添加到securityManager环境
    defaultSecurityManager.setRealm(iniRealm);

resources文件夹下新建一个文件user.ini用来存储用户信息和权限。

users下面是账号密码,“=”左边是账号,“=”右边是密码,密码后边是对应的权限,可以设置多个。roles下面是对应权限下的操作的权限。

[users]
Mark=123456,admin
[roles]
admin=delete

权限验证

 		//验证该用户是否有admin权限
        subject.checkRoles("admin");
        //验证该用户是否具有admin权限下的delete操作权限
	   //subject.checkPermission("delete"); //可以
        subject.checkPermission("add");     //不可以

该类完整实例

public class IniRealmTest {

    //创建一个IniRealm,指定账号密码的文件(resources文件夹下的user.ini文件)
    IniRealm iniRealm=new IniRealm("classpath:user.ini");

    @Test
    public void testAuthentication(){
        //1,构建securityManager环境
        DefaultSecurityManager defaultSecurityManager=new DefaultSecurityManager();
        //把上面的用户名密码设置添加到securityManager环境
        defaultSecurityManager.setRealm(iniRealm);
        //2,主体提交认证请求
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        Subject subject = SecurityUtils.getSubject();

        //这里是登录环境,获取到账号和密码,shiro会自动识别是否正确
        UsernamePasswordToken token=new UsernamePasswordToken("Mark","123456");
        //测试登录是否可以成功
        subject.login(token);
        boolean authenticated = subject.isAuthenticated();//true验证通过,false账号密码不正确
        System.err.println("是否可以登录"+authenticated);

        //验证该用户是否有admin权限
        subject.checkRoles("admin");
        //验证该用户是否具有admin权限下的delete操作权限
		// subject.checkPermission("delete"); //可以
        subject.checkPermission("add");     //不可以
    }
}

2,JdbcRealm的使用

首先需要加入Druid包和mysql驱动包。

		<dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.10</version>
        </dependency>

它是通过查询数据库表来进行操作的。

public class JdbcRealmTest {

    DruidDataSource dataSource=new DruidDataSource();
    {
        dataSource.setUrl("jdbc:mysql://localhost:3306/test?serverTimezone=UTC");
        dataSource.setUsername("root");
        dataSource.setPassword("1234");
    }

    @Test
    public void testAuthentication(){

        JdbcRealm jdbcRealm=new JdbcRealm();
        jdbcRealm.setDataSource(dataSource);
        //开启数据库授权,默认为关闭false
        jdbcRealm.setPermissionsLookupEnabled(true);
        //从数据库中test_user表中查找用户
        String sql="select password from test_user where account=?";
        jdbcRealm.setAuthenticationQuery(sql);

        //从数据库中test_user_role表中查询权限
        String roleSql="select user_role from test_user_role where user_name=?";
        jdbcRealm.setUserRolesQuery(roleSql);


        //1,构建securityManager环境
        DefaultSecurityManager defaultSecurityManager=new DefaultSecurityManager();
        //把上面的用户名密码设置添加到securityManager环境
        defaultSecurityManager.setRealm(jdbcRealm);

        //2,主体提交认证请求
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        Subject subject = SecurityUtils.getSubject();

        //这里是登录环境,获取到账号和密码,shiro会自动识别是否正确
        UsernamePasswordToken token=new UsernamePasswordToken("admin","123");
        //测试登录是否可以成功
        subject.login(token);
        boolean authenticated = subject.isAuthenticated();//true验证通过,false账号密码不正确
        System.err.println("是否可以登录"+authenticated);

        subject.checkRole("管理员权限");

    }
}

数据库表

test_user表(用户表)

在这里插入图片描述

test_user_role表(用户对应权限表)

在这里插入图片描述

打开JdbcRealm类查看源码,其实它已经默认封装了一些sql,如果不是自定义的话,可以默认使用它提供的sql,这样就可以不用手动写sql进行查询了。

在这里插入图片描述

3,自定义Realm

新建一个类,继承AuthorizingRealm类。

doGetAuthorizationInfo方法是授权方法。

步骤:

1,从数据库中或缓存中获取角色权限数据

2,获取操作权限数据

3,创建SimpleAuthorizationInfo并设置数据并返回

doGetAuthenticationInfo方法是认证方法

步骤:

1,从主体传过来的认证信息中,获得用户名

2,通过用户名到数据库中获取凭证

3,创建SimpleAuthenticationInfo返回对象(参数:用户名,密码,自定义Realm的名字)

public class CustomRealm extends AuthorizingRealm {
    //做授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

        String username= (String) principals.getPrimaryPrincipal();
       //1,从数据库中或缓存中获取角色权限数据
        Set<String> roles=getRolesByUsername(username);
        //再获取操作权限数据
        Set<String> permissions=getPermissionsByUsername(username);
        SimpleAuthorizationInfo simpleAuthorizationInfo=new SimpleAuthorizationInfo();
        simpleAuthorizationInfo.setStringPermissions(permissions);
        simpleAuthorizationInfo.setRoles(roles);

        return simpleAuthorizationInfo;
    }

    //模拟数据库根据用户名取出操作权限数据

    private Set<String> getPermissionsByUsername(String username) {
        Set<String> set=new HashSet<>();
        set.add("delete");
        return set;
    }

    //模拟数据库根据用户名取出权限数据
    private Set<String> getRolesByUsername(String username) {
        Set<String> set=new HashSet<>();
        set.add("管理员权限");
        return set;
    }

    //做认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //1,从主体传过来的认证信息中,获得用户名
        String username= (String) token.getPrincipal();
        //2,通过用户名到数据库中获取凭证
        String password=getPasswordByUsername(username);
        //创建SimpleAuthenticationInfo返回对象(参数:用户名,密码,自定义Realm的名字)
        SimpleAuthenticationInfo authenticationInfo=new SimpleAuthenticationInfo(username,password,"customRealm");
        return authenticationInfo;
    }

    //模拟数据库通过username拿password
    private String getPasswordByUsername(String username) {
        String password="123456";
        return password;
    }
}

写好自定义Realm之后,然后进行测试

还是上面的内容

直接把Realm添加到securityManager环境,运行可以通过。如果账号密码错误,或者没有对应权限,则报错。

 @Test
    public void testAuthentication(){
        CustomRealm customRealm=new CustomRealm();

        //1,构建securityManager环境
        DefaultSecurityManager defaultSecurityManager=new DefaultSecurityManager();
        //把上面的用户名密码设置添加到securityManager环境
        defaultSecurityManager.setRealm(customRealm);
        //2,主体提交认证请求
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        Subject subject = SecurityUtils.getSubject();

        //这里是登录环境,获取到账号和密码,shiro会自动识别是否正确
        UsernamePasswordToken token=new UsernamePasswordToken("Mark","123456");
        //测试登录是否可以成功
        subject.login(token);
        boolean authenticated = subject.isAuthenticated();//true验证通过,false账号密码不正确
        System.err.println("是否可以登录"+authenticated);

        //测试是否有对应权限
        subject.checkRoles("管理员权限");
        subject.checkPermission("delete");
    }

4,Shiro加密

在上面自定义Realm的基础上进行加密。

首先把Realm类中的密码设置成Md5加密后的密文。

	//模拟数据库通过username拿password
    private String getPasswordByUsername(String username) {
        Md5Hash md5Hash=new Md5Hash("123456");
        return  md5Hash.toString();
    }

然后把shiro加密设置到Realm中。

 		//shiro加密
        HashedCredentialsMatcher hashedCredentialsMatcher=new HashedCredentialsMatcher();
        //设置加密算法
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        //设置加密次数
        hashedCredentialsMatcher.setHashIterations(1);
        //把shiro加密设置到Realm中
        customRealm.setCredentialsMatcher(hashedCredentialsMatcher);

这样做的目的是因为数据库中的密码是加密后的密文,那么Shiro进行密码校验是否正确时,会和密文相比较是否正确。

运行结果当然为true

四,springboot整合shiro安全框架

shiro有三个核心内容

1,导入springboot集成shiro依赖

首先添加shiro整合spring的依赖包,页面跳转用到了thymeleaf,自己导依赖包。

		<dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.1</version>
        </dependency>

2,创建一个自定义Realm

public class UserRealm extends AuthorizingRealm {

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

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
       return null;
    }

3,创建ShiroConfig

在ShiroConfig类一共做三步操作:

第一步,创建Realm对象 ----连接数据
第二步,创建DefaultWebSecurityManager环境 ----关联Realm
第三步,创建ShiroFilterFactoryBean ----将第二部的环境设置进去

@Configuration
public class ShiroConfig{
    /**
     *
     *         Subject 用户
     *         SecurityManager  管理所有用户
     *         Realm  连接数据
     *
     * 第一步,创建Realm对象  ----连接数据
     * 第二步,创建DefaultWebSecurityManager环境    ----关联Realm
     * 第三步,创建ShiroFilterFactoryBean     ----将第二部的环境设置进去
     */

    //创建Realm对象,自定义类
    @Bean
    public UserRealm userRealm(){
        return new UserRealm();
    }

    // @Bean(name = "securityManager")是把该对象加到spring容器
    @Bean(name = "securityManager")
    public DefaultSecurityManager getDefaultSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
        DefaultSecurityManager securityManager=new DefaultWebSecurityManager();
        //关联Realm对象
        securityManager.setRealm(customRealm);
        return securityManager;
    }

    //@Qualifier("securityManager")是根据名字从spring容器中拿到该对象
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultSecurityManager defaultSecurityManager){
        ShiroFilterFactoryBean factoryBean=new ShiroFilterFactoryBean();
        //设置安全管理器
        factoryBean.setSecurityManager(defaultSecurityManager);
        return factoryBean;
    }
}

4,创建test页面

三个html页面

首页

<body>
欢迎进到首页!<br>
<a href="/add">add</a><br><a href="/update">update</a>
</body>

添加页面

<body>
添加页面
</body>

修改页面

<body>
修改页面
</body>

跳转页面的controller方法

 @RequestMapping("/")
    public String shouye(){
        return "index";
    }
    @RequestMapping("/add")
    public String add(){
        return "add";
    }
    @RequestMapping("/update")
    public String update(){
        return "update";
    }

准备工作完成。

五,Shiro实现登录拦截

先创建一个登录页面和跳转该登录页面的controller方法。

登录页面

<body>
<form action="/login" method="post">
   账号: <input type="text" ><br>
    密码:<input type="password"><br>
    <input type="submit" value="登录">
</form>
</body>

controller方法

	@RequestMapping("/login")
    public String login(){
        return "login";
    }

ShiroConfig类的ShiroFilterFactoryBean方法中写shiro的内置过滤器。

 @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultSecurityManager defaultSecurityManager){
        ShiroFilterFactoryBean factoryBean=new ShiroFilterFactoryBean();
        //设置安全管理器
        factoryBean.setSecurityManager(defaultSecurityManager);

        //添加shiro的内置过滤器
        /**
         * anon:无需认证就可以访问
         * authc:必须认证了才能访问
         * user:必须拥有  记住我 功能才可以访问
         * perms:拥有对某个资源的权限才能访问
         * role:拥有某个角色权限才能访问
         */
        Map<String,String> filterMap=new LinkedHashMap<>();
        //设置必须认证才能访问
        filterMap.put("/add","authc");
        filterMap.put("/update","authc");
//        filterMap.put("/update/*","authc");/*是通配符
        factoryBean.setFilterChainDefinitionMap(filterMap);

        //设置登录请求(必须认证才能访问,会自动跳转到下面的登录路径)
        factoryBean.setLoginUrl("/login");

        return factoryBean;
    }

实现的效果如下图

进入首页

在这里插入图片描述

点击add按钮和update按钮,会让你去登录页面。(认证才能访问)

在这里插入图片描述

六,Shiro实现用户认证

首先在UserRealm类的认证方法中写认证方法。

 	//认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("执行了认证---一执行登录方法就进到此认证方法中");
        //用户名  密码  数据库中取
        //这里做假数据
        String username="root";
        String password="123456";
        UsernamePasswordToken userToken=(UsernamePasswordToken)token;
        if(!userToken.getUsername().equals(username)){
            return null; //抛出异常 UnknownAccountException
        }

        //密码认证 是shiro自动管理密码的
        return new SimpleAuthenticationInfo(username,password,"UserRealm");
    }

然后写一个登录方法

	@RequestMapping("/denglu")
    public String denglu(String username, String password, Model model){
        //获取当前的用户
        Subject subject = SecurityUtils.getSubject();
        //封装用户的登录数据
        UsernamePasswordToken token=new UsernamePasswordToken(username,password);
        //执行登录方法,没有异常则OK
        try {
            subject.login(token);
            return "/index";
        }catch (UnknownAccountException e){
            model.addAttribute("msg","用户名不存在");
            return "/login";
        }catch (IncorrectCredentialsException e){
            model.addAttribute("msg","密码错误");
            return "/login";
        }
    }

登录表单

回顾一下登录页面

<body>
<span th:text="${msg}"></span>
<form action="/denglu" method="post">
   账号: <input type="text" name="username"><br>
    密码:<input type="password" name="password"><br>
    <input type="submit" value="登录">
</form>
</body>

实现的效果如下图

首先进到首页

在这里插入图片描述

点击add按钮或者update按钮,会让你去认证,跳转登录页面。

在这里插入图片描述

输入一个错误的用户名

在这里插入图片描述

输入一个错误的密码

在这里插入图片描述

输入正确,跳转到首页,认证成功。

在这里插入图片描述

七,Shiro整合mybatis

Shiro整合mybatis的过程就不讲了,重点是从数据库中的user账号密码表查询数据,而不是假数据。

依赖注入是在UserRealm的认证方法中,参考下面代码。

 	 //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("执行了认证---一执行登录方法就进到此认证方法中");
        //用户名  密码  数据库中取
        //这里做假数据
        /**
         * 当连接真实的数据库时,依赖注入service层以后,去数据库拿用户名对应的账号和密码那一条数据,参数是userToken.getUsername();
         * 比如
         * @Autowired
         * Service service;
         *
         *
         * User user=service.selectUserByUsername(userToken.getUsername());
         * if(user==null){ //没有这个人
         *     return null;   UnknownAccountException
         * }
         *
         * //密码加密  Md5  Md5盐值加密
         *
         *  return new SimpleAuthenticationInfo(user.getAccount(),user.getPwd(),"UserRealm");
         */
        String username="root";
        String password="123456";
        UsernamePasswordToken userToken=(UsernamePasswordToken)token;
        if(!userToken.getUsername().equals(username)){
            return null; //抛出异常 UnknownAccountException
        }

        //密码认证 是shiro自动管理密码的
        return new SimpleAuthenticationInfo(username,password,"UserRealm");
    }

八,Shiro请求授权实现

首先在ShiroConfig类的getShiroFilterFactoryBean方法中设置权限。

 @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultSecurityManager defaultSecurityManager){
        ShiroFilterFactoryBean factoryBean=new ShiroFilterFactoryBean();
        //设置安全管理器
        factoryBean.setSecurityManager(defaultSecurityManager);

        //添加shiro的内置过滤器
        /**
         * anon:无需认证就可以访问
         * authc:必须认证了才能访问
         * user:必须拥有  记住我 功能才可以访问
         * perms:拥有对某个资源的权限才能访问
         * role:拥有某个角色权限才能访问
         */
        Map<String,String> filterMap=new LinkedHashMap<>();
        //设置必须认证才能访问
        filterMap.put("/add","authc");
        filterMap.put("/update","authc");
//        filterMap.put("/update/*","authc");/*是通配符

        //#######################################设置访问权限开始####################################
        //授权  拥有对某个资源的权限才能访问
        //正常情况下,没有授权会跳转到未授权页面
        filterMap.put("/add","perms[user:add]");
		 //#######################################设置访问权限结束####################################
        factoryBean.setFilterChainDefinitionMap(filterMap);

        //设置登录请求(必须认证才能访问,会自动跳转到下面的登录路径)
        factoryBean.setLoginUrl("/login");
         //#######跳转未授权页面########
        //未授权页面
        factoryBean.setUnauthorizedUrl("/noauth");
		 //#######跳转未授权页面########
        return factoryBean;
    }

添加一个未授权页面的模拟方法

	@RequestMapping("/noauth")
    @ResponseBody
    public String noauth(){
        return "未经授权无法访问该页面";
    }

访问权限已经设置好了,那么在哪里给用户进行授权呢?

说白了,在自定义Realm中的授权方法中进行授权。

这里提及一点,很重要!在UserRealm的认证方法最后返回一个SimpleAuthenticationInfo对象(下面)。第二个参数是用户的密码,第三个是自定义Realm的名字,重点是第一个参数。

return new SimpleAuthenticationInfo(user,password,"UserRealm");

这个参数在认证方法中返回,然后会传到当前类Realm的授权方法中,接收该参数的方法

User currentUser =(User) subject.getPrincipal();

开始授权

前提

在自定义的UserRealm类的认证方法中查询到当前用户的信息之后要把这个用户信息对象放到第一个参数里传到授权方法中并接收。

在这里插入图片描述

然后再授权方法中拿到传过来的User对象。

在这里插入图片描述

在自定义的UserRealm类的授权方法中进行授权。

不要迷! currentUser.getPerms()只是实体类的属性,即拿到数据库保存权限的字段的值,并不是shiro的方法。

 //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("执行了授权---授权操作会首先进到此授权方法");
        SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
        //授权
        //这里每进来申请权限的用户都会添加这个user:add操作权限,实际开发中要判断用户身份添加不同的操作权限
        //这是写死的添加权限,连接数据库后,从数据库查询用户对应的权限,就是下面注释的内容,实现了动态授权
        info.addStringPermission("user:add");
        /**
         *         //拿到当前登录的对象
         *         Subject subject = SecurityUtils.getSubject();
         *         //拿到User对象  subject.getPrincipal()得到上面的第一个参数
         *         User currentUser =(User) subject.getPrincipal();
         *         //设置当前用户的权限
         *         info.addStringPermission(currentUser.getPerms());
         */
        return info;
    }

然后授权就完成了!

效果展示(在授权方法中没有执行授权时)

127.0.0.1:8088/index进入首页

在这里插入图片描述

点击按钮,转去认证(也就是登录)

在这里插入图片描述

登录成功,返回首页

在这里插入图片描述

再点击update按钮,进入修改页面

在这里插入图片描述

点击add按钮,跳转未授权页面。

在这里插入图片描述

UserRealm的授权方法中进行动态授权后(数据库查询用户对应的权限),就可以访问该页面了。

九,Shiro整合thymeleaf

这个业务场景是由于一些人没有一些权限,那么没有权限的操作列表就没有必要在页面上进行显示了,只有对应权限的用户才能看到该操作列表。

这里提一下shiro如何记录session

如果登录成功,记入session(此session是shiro环境的session,不是HttpSession)。在Realm类的认证方法中验证,如果用户密码正确,记入session。

在这里插入图片描述

添加shiro整合thymeleaf的依赖包

	    <!--shiro和thymeleaf整合-->
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>

然后在html标签添加,和thymeleaf的格式一样。

xmlns:shiro="http://www.pollix.at/thymeleaf/shiro

在ShiroConfig的配置类中加入Bean

@Bean
    public ShiroDialect getShiroDialect(){
        return new ShiroDialect();
    }

首页的html代码

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
        xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
欢迎进到首页!<br>
 <a shiro:hasPermission="add" href="/shiro/add">add</a>

    &nbsp;&nbsp;&nbsp;&nbsp;
    <a shiro:hasPermission="update" href="/shiro/edit">edit</a>

    &nbsp;&nbsp;&nbsp;&nbsp;
    <a style="float: right;margin-right: 30%;" href="/shiro/logout">退出</a>
</body>
</html>

说明:shiro:hasPermission="user:add"是当用户有user:add权限时才展示在页面,否则不展示。

到此结束!之后在项目中碰到问题会接着在这里补充!

posted @ 2020-03-08 17:27  你樊不樊  阅读(234)  评论(0编辑  收藏  举报