shiro密码加密原理与用户授权操作、整合Thymeleaf

密码比对原理探究

思考?这个Shiro,是怎么帮我们实现密码自动比对的呢? 我们可以去 realm的父类 AuthorizingRealm 的父类 AuthenticatingRealm 中找一个方法
核心:getCredentialsMatcher() 翻译过来:获取证书匹配器

我们去看这个接口 CredentialsMatcher 有很多的实现类,MD5盐值加密
在这里插入图片描述
在这里插入图片描述
我们的密码一般都不能使用明文保存?
需要加密处理;思路分析

  • 如何把一个字符串加密为MD5
  • 替换当前的Realm 的 CredentialsMatcher 属性,直接使用 Md5CredentialsMatcher 对象, 并设置加密算法

用户授权操作

使用shiro的过滤器来拦截请求即可!
1.在 ShiroFilterFactoryBean 中添加一个过滤器

  //授权过滤器
        filterMap.put("/user/add","perms[user:add]");
        filterMap.put("/user/update","perms[user:update]");

        filterMap.put("/user/*","authc");
        
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);

2.我们再次启动测试一下,访问add,发现以下错误!未授权错误!
在这里插入图片描述

3.注意:当我们实现权限拦截后,shiro会自动跳转到未授权的页面,但我们没有这个页面,所有401 了
4.配置一个未授权的提示的页面,增加一个controller提示

 @RequestMapping("/noauth")
    @ResponseBody
    public String noAuth(){
        return "没有权限访问该页面";
    }

然后再 shiroFilterFactoryBean 中配置一个未授权的请求页面!

    shiroFilterFactoryBean.setUnauthorizedUrl("/noauth");

5.测试,现在没有授权,可以跳转到我们指定的位置了!
在这里插入图片描述

Shiro授权

在UserRealm 中添加授权的逻辑,增加授权的字符串!

//执行授权逻辑
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("执行了=>授权逻辑PrincipalCollection");

        //给资源授权
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //添加资源的授权字符串
        info.addStringPermission("user:add");
        info.addStringPermission("user:update");

        return info;
    }

我们再次登录测试,发现登录的用户是可以进行访问页面了!授权成功!
问题,我们现在完全是硬编码,无论是谁登录上来,都可以实现授权通过,但是真实的业务情况应该 是,每个用户拥有自己的一些权限,从而进行操作,所以说,权限,应该在用户的数据库中,正常的情 况下,应该数据库中是由一个权限表的,我们需要联表查询,但是这里为了大家操作理解方便一些,我 们直接在数据库表中增加一个字段来进行操作
在这里插入图片描述
给数据库中的用户增加一些权限
在这里插入图片描述

1.修改实体类,增加一个字段

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {

    private String userName;
    private String password;
    private String perms;
}

2.我们现在需要再自定义的授权认证中,获取登录的用户,从而实现动态认证授权操作!
在用户登录授权的时候,将用户放在 Principal 中,改造下之前的代码

//2. 验证密码,我们可以使用一个AuthenticationInfo实现类 SimpleAuthenticationInfo    
        //   shiro会自动帮我们验证!重点是第二个参数就是要验证的密码
        return new SimpleAuthenticationInfo(user,user.getPassword(),"");

然后再授权的地方获得这个用户,从而获得它的权限

 //执行授权逻辑
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("执行了=>授权逻辑PrincipalCollection");

        //给资源授权
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //添加资源的授权字符串
       /* info.addStringPermission("user:add");
        info.addStringPermission("user:update");*/

        //获得当前对象
        Subject subject = SecurityUtils.getSubject();
        //拿到user对象
        User currentUser = (User) subject.getPrincipal();

        System.out.println(currentUser.getPerms());

        //设置权限
        info.addStringPermission(currentUser.getPerms());

        return info;
    }

我们启动项目,登录不同的账户,进行测试一下! 6. 测试完美通过OK

整合Thymeleaf

根据权限展示不同的前端页面
1.添加Maven的依赖

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

2.配置一个shiro的Dialect ,在shiro的配置中增加一个Bean

 //配置ShiroDialect:方言,用于 thymeleaf 和 shiro 标签配合使用
    @Bean
    public ShiroDialect getShiroDialect(){
        return new ShiroDialect();
    }

3.修改前端的配置

<!DOCTYPE html>
<html lang="en"
xmlns:th="http://www.thymeleaf.org"
      xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
<h1>首页</h1>

<p th:if="${session.loginUser==null}">
    <a th:href="@{/toLogin}">登录</a>
</p>


<p th:text="${msg}"></p>
<div shiro:hasPermission="user:add">
    <a th:href="@{/user/add}">add</a>
</div>

<div shiro:hasPermission="user:update">
    <a th:href="@{/user/update}">update</a>
</div>

</body>
</html>

4.我们在去测试一下,可以发现,现在首页什么都没有了,因为我们没有登录,我们可以尝试登录下 ,来判断这个Shiro的效果!登录后,可以看到不同的用户,有不同的效果,现在就已经接近完美 了~!还不是完美

5.为了完美,我们在用户登录后应该把信息放到Session中,我们完善下!在执行认证逻辑时候,加 入session

  Subject subject = SecurityUtils.getSubject();
        subject.getSession().setAttribute("loginUser",user);

6.前端从session中获取,然后用来判断是否显示登录

<p th:if="${session.loginUser==null}">
    <a th:href="@{/toLogin}">登录</a>
</p>

7.测试,效果完美~
在这里插入图片描述
在这里插入图片描述

完整pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.4.2</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<groupId>com.loey</groupId>
	<artifactId>shiro-02-springboot</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>shiro-02-springboot</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>11</java.version>
	</properties>

	<dependencies>

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

		<!-- 引入 myBatis,这是 MyBatis官方提供的适配 Spring Boot 的,而不是Spring Boot自己的-->
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>2.1.4</version>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>

		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.17</version>
		</dependency>

		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid</artifactId>
			<version>1.1.12</version>
		</dependency>

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<version>1.18.16</version>
		</dependency>
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-spring</artifactId>
			<version>1.7.0</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

完整UserRealm.java

package com.loey.config;

import com.loey.pojo.User;
import com.loey.service.UserService;
import org.apache.catalina.security.SecurityUtil;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;

//自定义Realm
public class UserRealm extends AuthorizingRealm {

    @Autowired
    UserService userService;

    //执行授权逻辑
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("执行了=>授权逻辑PrincipalCollection");

        //给资源授权
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //添加资源的授权字符串
       /* info.addStringPermission("user:add");
        info.addStringPermission("user:update");*/

        //获得当前对象
        Subject subject = SecurityUtils.getSubject();
        //拿到user对象
        User currentUser = (User) subject.getPrincipal();

        //设置权限
        info.addStringPermission(currentUser.getPerms());

        return info;
    }

    //执行认证逻辑
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("执行了=>认证逻辑AuthenticationToken");

        //1.判断用户名
        UsernamePasswordToken userToken = (UsernamePasswordToken) token;

        //真实连接数据库
        User user = userService.queryUserByName(userToken.getUsername());
        if(user == null){
            //用户名不存在
            return null;//shiro底层会抛出 UnknownAccountException
        }

        Subject subject = SecurityUtils.getSubject();
        subject.getSession().setAttribute("loginUser",user);

        //2. 验证密码,我们可以使用一个AuthenticationInfo实现类 SimpleAuthenticationInfo    
        //   shiro会自动帮我们验证!重点是第二个参数就是要验证的密码
        return new SimpleAuthenticationInfo(user,user.getPassword(),"");
    }
}

完整ShiroConfig.java

package com.loey.config;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.Map;

//声明为配置类
@Configuration
public class ShiroConfig {

     //创建 ShiroFilterFactoryBean
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
         ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

         //设置安全管理器    
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        Map<String, String> filterMap = new LinkedHashMap<String,String>();

         /*  
                添加Shiro内置过滤器,常用的有如下过滤器:          
                anon: 无需认证就可以访问            
                authc: 必须认证才可以访问  
                user: 如果使用了记住我功能就可以直接访问   
                perms:  拥有某个资源权限才可以访问
                role: 拥有某个角色权限才可以访问     
          */
//        filterMap.put("/user/add","authc");
//        filterMap.put("/user/update","authc");

        //授权过滤器
        filterMap.put("/user/add","perms[user:add]");
        filterMap.put("/user/update","perms[user:update]");

        filterMap.put("/user/*","authc");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);

        shiroFilterFactoryBean.setUnauthorizedUrl("/noauth");


        //修改要跳转的login页面
        shiroFilterFactoryBean.setLoginUrl("/toLogin");
        return shiroFilterFactoryBean;
    }


     //创建 DefaultWebSecurityManager
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

        //关联Realm
        securityManager.setRealm(userRealm);
        return securityManager;
    }

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

    //配置ShiroDialect:方言,用于 thymeleaf 和 shiro 标签配合使用
    @Bean
    public ShiroDialect getShiroDialect(){
        return new ShiroDialect();
    }
}

MyController.java

//登录操作
    @RequestMapping("/login")
    public String login(String username,String password,Model model){

        //使用shiro编写认证操作

        //1.获取Subject(当前用户)
        Subject subject = SecurityUtils.getSubject();

        //2.封装用户的数据
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);

        //3.执行登录的方法,只要没有异常就代表登录成功
        try {
            subject.login(token);
            return "index";
        }catch (UnknownAccountException uae) {//如果没有指定的用户,则 UnknownAccountException异常
            model.addAttribute("msg","用户名不存在");
            return "login";
        } catch (IncorrectCredentialsException ice) {//密码不对的异常
            model.addAttribute("msg","密码错误");
            return "login";
        }

    }

    @RequestMapping("/noauth")
    @ResponseBody
    public String noAuth(){
        return "没有权限访问该页面";
    }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值