Shiro框架初步认识

序言:

据初步了解, 虽然spring security框架功能强大, 但Shiro框架相比于spring security框架更加简单, 功能也较齐全了, 是更多人用的安全框架。

注:这个项目是结合Mybatis做的, Mybatis相关配置看 Mybatis 的文章。

一、相关依赖:

	<!-- shiro -->
	<dependency>
		<groupId>org.apache.shiro</groupId>
		<artifactId>shiro-spring</artifactId>
		<version>1.3.2</version>
	</dependency>
	
	<!--shiro与thymeleaf结合的依赖 -->
	<!-- https://mvnrepository.com/artifact/com.github.theborakompanioni/thymeleaf-extras-shiro -->
	<dependency>
	    <groupId>com.github.theborakompanioni</groupId>
	    <artifactId>thymeleaf-extras-shiro</artifactId>
	    <version>2.0.0</version>
	</dependency>

二、配置文件:

spring:
  datasource:
    username: root
    password: admin
    url: jdbc:mysql://localhost:3306/abc?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false
    driver-class-name: com.mysql.cj.jdbc.Driver
#    type: com.albaba.druid.pool.DruidDataSource

mybatis:
  //mybatis配置文件路径
  config-location: classpath:mybatis/mybatis-config.xml 
  //mapper文件路径
  mapper-locations: classpath:mybatis/mapper/*.xml

三、Shiro配置文件:

ShiroConfig类:

package com.example.config;

import java.util.HashMap;
import java.util.Map;

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 at.pollux.thymeleaf.shiro.dialect.ShiroDialect;

@Configuration
public class ShiroConfig {
	
	//拦截器, 拦截到相关映射地址后跳转到登录页面
	@Bean
	public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		
		//设置安全管理器
		shiroFilterFactoryBean.setSecurityManager(securityManager);
		
		/**
		 * Shiro内置过滤器, 实现相关权限相关的拦截器
		 * 		常用的过滤器:
		 * 			anon: 无需认证(登录)可以访问
		 * 			authc: 必须认证才可以访问
		 * 			user: 如果使用rememberMe的功能可以直接访问
		 *  		perms: 该资源必须得到资源权限才可以访问
		 *  		role: 该资源必须得到角色权限才可以访问
		 */
		Map<String, String> filterMap = new HashMap<String, String>();
		
		
//		filterMap.put("/add", "authc");
//		filterMap.put("/update", "authc");
		filterMap.put("/hello", "anon");
		filterMap.put("/login", "anon");
		
		filterMap.put("/**", "authc");
		//授权拦截器, 拦截后会调用 doGetAuthorizationInfo 方法进行授权逻辑
		filterMap.put("/test/add", "perms[user:abc]");
		filterMap.put("/test/update", "perms[user:add]");
		//拦截 /test/update 这种映射地址,但这样写授权拦截器会无效, 我也不晓得
//		filterMap.put("/test/**", "authc");
		
		//设置自定义登录认证页面
		shiroFilterFactoryBean.setLoginUrl("/toLogin");
		//设置自定义未授权页面
		shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorizedUrl");
		
		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
		
		
		return shiroFilterFactoryBean;
	}
	

	@Bean("securityManager")
	public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm){
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		securityManager.setRealm(userRealm);
		return securityManager;
	}
	

	@Bean("userRealm")
	public UserRealm getRealm(){
		return new UserRealm();
	}
	
	/**
	 * 配置ShiroDialect, 用于thymeleaf和shiro标签配合使用
	 */
	@Bean
	public ShiroDialect getShiroDialect(){
		return new ShiroDialect();
	}
}


UserRealm类:

package com.example.config;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
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;

import com.example.bean.User;
import com.example.service.UserService;

public class UserRealm extends AuthorizingRealm{
	
	@Autowired
	UserService userService;
	
	//授权
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
		System.out.println("执行授权逻辑");
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
		
		//获取当前登录用户
		Subject subject = SecurityUtils.getSubject();
		//是从doGetAuthenticationInfo方法返回的user得到的
		System.out.println("subject.getPrincipal();" + subject.getPrincipal());
		User user = (User) subject.getPrincipal();
		User dbUser = userService.findById(user.getId());
		//添加资源的授权字符串
		System.out.println(user.getPerms());
		info.addStringPermission(user.getPerms());
		return info;
	}
	
	//认证
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {
		System.out.println("执行认证逻辑");

		//subject.login(token) 传来的token
		UsernamePasswordToken token = (UsernamePasswordToken) arg0;
		
		//判断用户名是否存在
		User user = userService.findByuserName(token.getUsername());
		if(user==null){
			//用户名不存在
			//返回 null, shiro 底层会抛出UnKnowAccountException异常
			return null;
		}
		
		/*
		 * 2、判断密码是否正确 
		 * 1)、错误的话 shiro 底层会抛出IncorrectCredentialsException异常
		 * 2)、正确的话返回true
		 */
		return new SimpleAuthenticationInfo(user/*方法的携带信息*/, user.getPassword(), ""/*shiro的名称*/);
	}

}

	@RequestMapping("/login")
	public String login(String name, String password, Model model){
		/**
		 * 使用Shiro对页面的账号与密码进行认证
		 */
		//1、获取Subject
		Subject subject = SecurityUtils.getSubject();
		
		//2、封装用户数据, 形成一个令牌
		UsernamePasswordToken token = new UsernamePasswordToken(name, password);
		
		//3、执行登录方法
		try{
			//执行此方法时会调用到 doGetAuthenticationInfo 认证方法
			subject.login(token);
			
			//登录成功
			//成功后 shiro 会保存session, session在就不会再拦截了
			return "success";
		}
		catch(UnknownAccountException e){
			//登录失败
			model.addAttribute("msg", "用户不存在");
			return "forward:/toLogin";
		}
		catch(IncorrectCredentialsException e){
			//登录失败
			model.addAttribute("msg", "密码错误");
			return "forward:/toLogin"; 
		}
	}

四、自定义验证登录页面:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>登陆页面</h1>
<font th:text="${msg}" style="color: red"></font>
<form name="f" action="login" method="post">
<br/>
用户名:
<input type="text" name="name" value="root" placeholder="name"><br/>
密码:
<input type="password" name="password" value="admin" placeholder="password"><br/>
<input type="checkbox" name="remember">记住我<br/>
<input name="submit" type="submit" value="提交">
</form>
</body>
</html>

五、Shiro与thymeleaf简单的权限标签:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1 th:text="${name}">name</h1>
<div shiro:hasPermission="user:abc">
进入用户添加功能:<a href="test/add">用户添加</a>
</div>
<div shiro:hasPermission="user:add">
进入用户更新功能:<a href="test/update">用户更新</a>
</div>
<div>
登录:<a href="tologin">login</a>
</div>
</body>
</html>

注:这里的 user:abc 我觉得大概只是代表了权限, 并不代表角色与权限, 角色与权限的关系是多对多的, 或许后面我会再更新一下

退出

	//获取当前 subject 对象
	Subject subject = SecurityUtils.getSubject();
	//判断是否有验证过
	if(subject.isAuthenticated())
	//有验证即登录了就退出
	   subject.logout();
发布了52 篇原创文章 · 获赞 1 · 访问量 1747

猜你喜欢

转载自blog.csdn.net/qq_42039738/article/details/104367170