ICAT技术 阅读:910评论: 8 2018-07-30

shrio框架是一个开源项目,配置完成后,使用起来极其方便,供了认证、授权、加密 权限控制。

第一步:配置Shiro

(1),导入相关的包

<?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>com.ezquant</groupId>
	<artifactId>ezquant-admin</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>ezquant-admin</name>
	<description>Demo project for Spring Boot</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.9.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>	<!-- 添加web依赖 ,包含spring和springmvc等 -->
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>	<!-- 添加对jpa的支持,包含spring-data和Hibernate -->
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>	<!-- mysql连接的jar包 -->
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-tomcat</artifactId>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>	<!-- 因为SpringBoot内嵌的tomcat不支持jsp页面,同时SpringBoot也不推荐用jsp -->
			<groupId>org.apache.tomcat.embed</groupId>
			<artifactId>tomcat-embed-jasper</artifactId>
			<scope>provided</scope>
		</dependency>
		<dependency>	<!-- jsp标签库 -->
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
		</dependency>
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-spring</artifactId>
			<version>1.3.2</version>
		</dependency>
		<dependency>
			<groupId>org.sitemesh</groupId>
			<artifactId>sitemesh</artifactId>
			<version>3.0.1</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
		</dependency>
		<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.47</version>
		</dependency>
	</dependencies>

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


</project>

由于springboot不支持jsp,而Shiro只能在jsp内应用Shiro库下才能使用权限页面显示控制。

所以,引入jsp相关的依赖支持

(2)配置Shiro核心类

package com.ezquant.config;

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

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

/**
 * Shiro 配置,重点
 * 
 * @ClassName: ShiroSpringConfig
 * @Description: TODO
 * @author icat
 * @date 2018年6月14日 下午4:18:13
 *
 */
@Configuration
public class ShiroSpringConfig {

	private static final transient Logger log = LoggerFactory.getLogger(ShiroSpringConfig.class);

	/**
	 * 配置拦截器
	 * 
	 * 定义拦截URL权限,优先级从上到下 1). anon : 匿名访问,无需登录 2). authc : 登录后才能访问 3). logout: 登出 4).
	 * roles : 角色过滤器
	 * 
	 * URL 匹配风格 1). ?:匹配一个字符,如 /admin? 将匹配 /admin1,但不匹配 /admin 或 /admin/; 2).
	 * *:匹配零个或多个字符串,如 /admin* 将匹配 /admin 或/admin123,但不匹配 /admin/1; 2).
	 * **:匹配路径中的零个或多个路径,如 /admin/** 将匹配 /admin/a 或 /admin/a/b
	 * 
	 * 配置身份验证成功,失败的跳转路径
	 */
	@Bean
	public ShiroFilterFactoryBean shirFilter(DefaultWebSecurityManager securityManager) {
		log.info("^^^^^^^^^^^^^^^^^^^^  配置Shiro拦截工厂");
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		shiroFilterFactoryBean.setSecurityManager(securityManager);
		Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
		filterChainDefinitionMap.put("/views/static/**", "anon"); // 静态资源匿名访问
		filterChainDefinitionMap.put("/login/*", "anon");// 登录匿名访问
		filterChainDefinitionMap.put("/logout", "logout"); // 用户退出,只需配置logout即可实现该功能
		filterChainDefinitionMap.put("/**", "authc"); // 其他路径均需要身份认证,一般位于最下面,优先级最低
		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
		shiroFilterFactoryBean.setLoginUrl("/login"); // 登录的路径
		shiroFilterFactoryBean.setSuccessUrl("/"); // 登录成功后跳转的路径
		shiroFilterFactoryBean.setUnauthorizedUrl("/login"); // 验证失败后跳转的路径
		return shiroFilterFactoryBean;
	}

	/**
	 * 配置Shiro生命周期处理器
	 */
	@Bean
	public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
		return new LifecycleBeanPostProcessor();
	}

	/**
	 * 自动创建代理类,若不添加,Shiro的注解可能不会生效。
	 */
	@Bean
	@DependsOn({ "lifecycleBeanPostProcessor" })
	public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
		DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
		advisorAutoProxyCreator.setProxyTargetClass(true);
		return advisorAutoProxyCreator;
	}

	/**
	 * 开启Shiro的注解
	 */
	@Bean
	public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
		AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
		authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
		return authorizationAttributeSourceAdvisor;
	}

	/**
	 * 配置加密匹配,使用MD5的方式,进行1024次加密
	 */
	@Bean
	public HashedCredentialsMatcher hashedCredentialsMatcher() {
		HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
		hashedCredentialsMatcher.setHashAlgorithmName("MD5");
		hashedCredentialsMatcher.setHashIterations(1024);
		return hashedCredentialsMatcher;
	}

	/**
	 * 自定义Realm,可以多个
	 */
	@Bean
	public UserShiroRealm itDragonShiroRealm() {
		UserShiroRealm itDragonShiroRealm = new UserShiroRealm();
		itDragonShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
		return itDragonShiroRealm;
	}

	/**
	 * SecurityManager 安全管理器;Shiro的核心
	 */
	@Bean
	public DefaultWebSecurityManager securityManager() {
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		securityManager.setRealm(itDragonShiroRealm());
		return securityManager;
	}

}

(3)配置自定义安全数据Realm

package com.ezquant.config;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
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.util.ByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.ezquant.interfaces.SysUserRepository;
import com.ezquant.model.SysUser;

/**
 * 自定义安全数据Realm
 * 
 * @ClassName: ITDragonShiroRealm
 * @Description: TODO
 * @author icat
 * @date 2018年6月14日 下午4:11:46
 *
 */
public class UserShiroRealm extends AuthorizingRealm {

	private static final transient Logger log = LoggerFactory.getLogger(UserShiroRealm.class);

	@Autowired
	private SysUserRepository userRepository;// jpa

	/**
	 * 授权
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		log.info("^^^^^^^^^^^^^^^^^^^^ 配置当前用户权限");
		String username = (String) principals.getPrimaryPrincipal();
		SysUser user = userRepository.findByAccount(username);// 从数据库根据账号查询用户
		if (null == user) {
			return null;
		}
		SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
		String[] role = user.getRole().split(",");// 角色
		String[] permission = user.getPermission().split(",");// 权限
		for (int i = 0; i < role.length; i++) {
			authorizationInfo.addRole(role[i]); // 添加具体角色
		}
		for (int i = 0; i < permission.length; i++) {
			authorizationInfo.addStringPermission(permission[i]);// 添加权限
		}
		return authorizationInfo;
	}

	/**
	 * 身份认证
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		log.info("^^^^^^^^^^^^^^^^^^^^ 认证用户身份信息");
		// 获取用户的输入的账号.
		String username = (String) token.getPrincipal();

		SysUser userInfo = userRepository.findByAccount(username);// 从数据库根据账号查询用户
		if (userInfo == null)
			throw new UnknownAccountException();
		if (userInfo.getStatus().equals("0")) {// 从数据库获取该用户的状态
			throw new LockedAccountException(); // 帐号锁定
		}
		SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username, // 用户名
				userInfo.getPassword(), // 密码
				ByteSource.Util.bytes(userInfo.getSalt()), // salt=username+salt
				getName() // realm name
		);
		return authenticationInfo;
	}
}

(4)配置异常shrio异常处理类

package com.ezquant.controller;

import javax.servlet.http.HttpServletRequest;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

/**
 * 异常统一处理
 * 
 * @ClassName: ExceptionController
 * @Description: TODO
 * @author icat
 * @date 2018年6月14日 下午4:03:46
 *
 */
@ControllerAdvice
public class ExceptionController {

	@ExceptionHandler(org.apache.shiro.authz.AuthorizationException.class)
	public String handleException(RedirectAttributes redirectAttributes, Exception exception,
			HttpServletRequest request) {
		redirectAttributes.addFlashAttribute("message", "抱歉!您没有权限执行这个操作,请联系管理员!");
		// String url = WebUtils.getRequestUri(request);
		// + url.split("/")[1]
		return "redirect:/";//权限不足跳转到的页面
	}

}


配置结构图如下。需要配置这三个类,shrio就算配置完成了。

image.png

数据库表结构

image.pngimage.png


第二步:使用Shiro做认证

@ResponseBody
	@RequestMapping(value = "/login/post")
	public JSONObject loginPost(@RequestParam("username") String username, @RequestParam("password") String password) {
		JSONObject object = new JSONObject();
		Subject currentUser = SecurityUtils.getSubject();
		if (!currentUser.isAuthenticated()) {
			UsernamePasswordToken token = new UsernamePasswordToken(username, password);
			try {
				currentUser.login(token); // 执行登录
				object.put("status", 200);
				object.put("msg", "SUCCESS");
				return object;
			} catch (LockedAccountException lae) {
				token.clear();
				object.put("status", 500);
				object.put("msg", "用户已经被锁定不能登录,请与管理员联系!");
				return object;
			} catch (AuthenticationException ae) {
				log.info("^^^^^^^^^^^^^^^^^^^^  登录失败: " + ae.getMessage());
				ae.printStackTrace();
				object.put("status", 500);
				object.put("msg", "账号密码不匹配");
				return object;
			}
		}
		return object;

	}


第三步:使用Shiro做权限控制

在jsp页面引入

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags"%>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
<shiro:hasRole name="admin">
<li class="layui-nav-item layui-this"><a href="/views/pages/userRegister.jsp" target="iframe">注册用户管理</a></li>
</shiro:hasRole>

(1)角色控制页面显示

在页面中添加<shiro:hasRole name="admin">标签标示,其中name值是需要的角色值,该登录的用户必须是admin角色

image.png 数据库存入的role的值下为admin所有该标签就能显示.

image.png


(2)权限控制页面显示

<shiro:hasPermission name="update">
<li class="layui-nav-item"><a href="/views/pages/agent.jsp" target="iframe">代理商管理</a></li>
</shiro:hasPermission>

该标签标示权限具有update,才能才操作。

(3)在后台接口做权限控制

@RequiresPermissions("update")
	@RequestMapping(value = "/update")
	public String update() {
		return "update";
	}

标示只有更新权限的才能访问此接口。

简单的权限控制和认证授权就配置使用完成了

转载请注明来源:

评论