Shiro认证和受权

shiro是一款权限控制的后台框架,能够控制用户-角色-权限的分配,例如资源和路径等等。html

主要内容:java

ShiroFilterFactoryBean工厂类mysql

SecurityManager安全管理器web

MyRealm extends AuthorizingRealm自定义Realm,实现doGetAuthenticationInfo(认证)和doGetAuthorizationInfo(受权)方法。spring

还有其余一些rememberMe、Session和Cache功能。sql

下面使用SpringBoot整合Shiro的例子简单说明apache

表:数据中用户密码要存储加密后的密文安全

pom.xml文件cookie

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>online.myson</groupId>
	<artifactId>shiro</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>Shiro</name>
	<description>Demo project for Spring Boot</description>

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

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<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>

		<!-- shiro -->
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-spring</artifactId>
			<version>1.4.0</version>
		</dependency>
		<!-- Spring Data JPA -->
		<dependency>
		    <groupId>org.springframework.boot</groupId>
		    <artifactId>spring-boot-starter-data-jpa</artifactId>
		    <version>2.0.1.RELEASE</version>
		</dependency>
		<!-- mysql -->
		<dependency>
		    <groupId>mysql</groupId>
		    <artifactId>mysql-connector-java</artifactId>
		    <version>5.1.45</version>
		</dependency>
		
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
		</dependency>
	</dependencies>

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


</project>

shiro配置类app

package online.myson.config;

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

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.util.ByteSource;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import online.myson.shiro.MyRealm;

@Configuration
public class ShiroConfig {

	/**
	 * 注册ShiroFilterFactoryBean,实际上是注册一个filter
	 */
	@Bean
	public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
		
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		// 设置securityManager
		shiroFilterFactoryBean.setSecurityManager(securityManager);
		
		// 设置拦截器
		Map<String,String> filterChainDefinitionMap = new LinkedHashMap<>();
		// 过滤链定义,从上向下执行,通常将/**放最下面
		// authc:全部的url都必须认证经过才能够访问;anon:全部url均可以匿名访问
		filterChainDefinitionMap.put("/statics/**", "anon");
		filterChainDefinitionMap.put("/login.html", "anon");
		filterChainDefinitionMap.put("/login", "anon");
		filterChainDefinitionMap.put("/user/**", "user");
		filterChainDefinitionMap.put("/*", "authc");
		// 不设置自动寻找根目录下的/login.jsp页面
		shiroFilterFactoryBean.setLoginUrl("/index");
		
		// 登陆成功后要跳转的链接
		shiroFilterFactoryBean.setSuccessUrl("/index");
		
		// 未受权页面
		shiroFilterFactoryBean.setUnauthorizedUrl("/403");
		
		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
		return shiroFilterFactoryBean;	
	}
	
	/**
	 * 注册securityManager
	 */
	@Bean
	public SecurityManager securityManager() {
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		securityManager.setRealm(myRealm());
		securityManager.setRememberMeManager(cookieRememberMeManager());
		return securityManager;
	}
	
	/**
	 * 注册自定义Realm
	 */
	@Bean
	public MyRealm myRealm() {
		MyRealm myRealm = new MyRealm();
		myRealm.setCredentialsMatcher(hashedCredentialsMatcher());
		return myRealm;
	}
	
	/**
	 * 注册HashedCredentialsMatcher,加解密
	 */
	@Bean
	public HashedCredentialsMatcher hashedCredentialsMatcher() {
		HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
		
		hashedCredentialsMatcher.setHashAlgorithmName("md5");
		hashedCredentialsMatcher.setHashIterations(2);
		
		return hashedCredentialsMatcher;
		
	}
	
	/**
	 * 注册CookieRememberMeManager
	 */
	@Bean
	public CookieRememberMeManager cookieRememberMeManager() {
		CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
		cookieRememberMeManager.setCookie(simpleCookie());
		return cookieRememberMeManager;
	}
	
	/**
	 * 注册SimpleCookie
	 */
	@Bean
	public SimpleCookie simpleCookie() {
		SimpleCookie simpleCookie = new SimpleCookie();
		simpleCookie.setPath("/");
		simpleCookie.setHttpOnly(true);
		simpleCookie.setMaxAge(604800);// 时间
		return simpleCookie;
	}
	
	/**
	 * 开启shiro aop支持
	 * 用于受权
	 */
	@Bean
	public AuthorizationAttributeSourceAdvisor AuthorizationAttributeSourceAdvisor(
			SecurityManager securityManager) {
		AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor
						= new AuthorizationAttributeSourceAdvisor();
		authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
		return authorizationAttributeSourceAdvisor;
	}
	
	public static void main(String[] args) {
		Object obj = new SimpleHash("md5","123456",ByteSource.Util.bytes("user"),2);
		System.out.println(obj);
	}
}

 

一、认证

// 认证
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		// 获取用户输入的帐号
		String username = (String)token.getPrincipal();
		System.out.println(token.getCredentials());
		
		// 查询是否存在帐号
		SysUser user = sysUserService.findByUsername(username);
		System.out.println(user);
		if(user == null) {
			return null;
		}
		
		// 验证密码
		SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
					user,
					user.getPassword(),
					ByteSource.Util.bytes(user.getSalt()),// salt
					"MyRealm"// realmName
				);
		
		return authenticationInfo;
	}

Controller类:

@PostMapping("/login")
	public ModelAndView login(SysUser user) {
		ModelAndView mv = new ModelAndView("/login");
		String msg = "成功";
		
		Subject subject = SecurityUtils.getSubject();
		
		UsernamePasswordToken token = 
				new UsernamePasswordToken(user.getUsername(), user.getPassword());
		
		try {
			subject.login(token);
		}catch(Exception e) {
			if(e instanceof UnknownAccountException) {
				msg = "帐户不存在";
			}else if(e instanceof IncorrectCredentialsException) {
				msg = "密码不正确";
			}else {
				msg = e.getMessage();
			}
		}
		
		mv.addObject("msg", msg);
		return mv;
	}

二、受权

三种方式:

a、经过Subject.isPermitted("userInfo:view")和subject.hasRole("admin")的一些方法,if else来控制

b、经过注解AOP控制

c、jsp页面使用标签<shiro>控制

// 受权
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

		SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
		SysUser user = (SysUser) principals.getPrimaryPrincipal();
		
		// 获取用户的角色和对应的权限,这里一次性查角色和权限了,也能够写sql查
		for(SysRole role : user.getRoleList()) {
			authorizationInfo.addRole(role.getRole());
			for(SysPermission permission : role.getPermissions()) {
				authorizationInfo.addStringPermission(permission.getPermission());
			}
		}
		
		return authorizationInfo;
	}

Controller:

/**
	 * 用户添加
	 */
	@GetMapping("/user/add")
	@RequiresPermissions("userInfo:view")
	public String userAdd() {
		Subject subject = SecurityUtils.getSubject();
//		if(subject.isPermitted("userInfo:view")) {
//			return "userAdd";
//		}else {
//			return "no authc";
//		}
		return "userAdd";