springboot+shiro实现登陆过滤、权限认证、shiro密码加密、ehcache缓存(附demo项目)

首先说一下shiro能干什么

     Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码学和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。(百度百科)

主要功能

        三个核心组件:Subject, SecurityManager 和 Realms.

Subject:即“当前操作用户”。但是,在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。但考虑到大多数目的和用途,你可以把它认为是Shiro的“用户”概念。
  Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。
  SecurityManager:它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。
  Realm: Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。
  从这个意义上讲,Realm实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权。配置多个Realm是可以的,但是至少需要一个。
  Shiro内置了可以连接大量安全数据源(又名目录)的Realm,如LDAP、关系数据库(JDBC)、类似INI的文本配置资源以及属性文件等。如果缺省的Realm不能满足需求,你还可以插入代表自定义数据源的自己的Realm实现。


(文章最后附有本文的 密码加密算法 以及 本项目的demo资源-idea的)

目录

demo项目结构

  pom文件引入依赖

ShiroConfigBean Shiro配置文件(java) 

realms(*)

           Ahandler

       LoginHandler 

service业务逻辑

 exception全局异常捕捉

entity实体类

ehcache 进行缓存权限数据 配置

html代码

测试的话 

对应代码资源         https://download.csdn.net/download/qq_32786139/10661538

本文密码加密算法



demo项目结构

 

 

springboot+shiro实现登陆过滤、权限认证、shiro密码加密、ehcache缓存(附demo项目)_第1张图片

代码

  pom文件引入依赖


    
        
            org.springframework.boot
            spring-boot-starter-aop
        
        
            org.springframework.boot
            spring-boot-starter-thymeleaf
        
        
            org.springframework.boot
            spring-boot-starter-web
        

        
            org.springframework.boot
            spring-boot-devtools
            runtime
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        
        
            log4j
            log4j
            1.2.17
        
        
            org.aspectj
            aspectjrt
            1.8.13
        
        
            org.aspectj
            aspectjweaver
            1.8.13
        
        
            cglib
            cglib
            2.2.2
        

        
        
            org.apache.shiro
            shiro-core
            1.2.2
        
        
            org.apache.shiro
            shiro-spring
            1.2.2
        
        
            org.apache.shiro
            shiro-ehcache
            1.2.2
        
        
        
            org.springframework
            spring-context-support
        

        
        
            commons-io
            commons-io
            2.4
        
    

ShiroConfigBean Shiro配置文件(java) 


import com.example.demo.realms.MyShiroRealm;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

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

/**
 * Shiro配置Bean
 */
@Configuration
public class ShiroConfigBean {

	@Bean
	public ShiroFilterFactoryBean shirFilter(DefaultWebSecurityManager securityManager) {
		System.out.println("ShiroConfiguration.shirFilter()");
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

		// 必须设置 SecurityManager
		shiroFilterFactoryBean.setSecurityManager(securityManager);
		// 拦截器.
		Map filterChainDefinitionMap = new LinkedHashMap();
		// 设置login URL
		shiroFilterFactoryBean.setLoginUrl("/login");
		// 登录成功后要跳转的链接
		shiroFilterFactoryBean.setSuccessUrl("/LoginSuccess.action");
		// 未授权的页面
		shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized.action");
		// src="jquery/jquery-3.2.1.min.js" 生效
		filterChainDefinitionMap.put("/jquery/*", "anon");
		// 设置登录的URL为匿名访问,因为一开始没有用户验证
		filterChainDefinitionMap.put("/login.action", "anon");
		
		filterChainDefinitionMap.put("/Exception.class", "anon");
		// 我写的url一般都是xxx.action,根据你的情况自己修改
		filterChainDefinitionMap.put("/*.action", "authc");
		// 退出系统的过滤器
		filterChainDefinitionMap.put("/logout", "logout");
		// 现在资源的角色
		filterChainDefinitionMap.put("/admin.html", "roles[admin]");
		// filterChainDefinitionMap.put("/user.html", "roles[user]");
		// 最后一班都,固定格式
		filterChainDefinitionMap.put("/**", "authc");
		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
		return shiroFilterFactoryBean;
	}

	/*
	 * 凭证匹配器 (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了
	 * 所以我们需要修改下doGetAuthenticationInfo中的代码; )
	 */
	@Bean
	public HashedCredentialsMatcher hashedCredentialsMatcher() {
		HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
		hashedCredentialsMatcher.setHashAlgorithmName("md5");// 散列算法:这里使用MD5算法;
		hashedCredentialsMatcher.setHashIterations(1024);// 散列的次数,比如散列两次,相当于md5(md5(""));
		return hashedCredentialsMatcher;
	}

	@Bean
	public MyShiroRealm myShiroRealm() {
		MyShiroRealm myShiroRealm = new MyShiroRealm();
		myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
		return myShiroRealm;
	}

	@Bean
	public DefaultWebSecurityManager securityManager() {
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		// 注入自定义的realm;
		securityManager.setRealm(myShiroRealm());
		// 注入缓存管理器;
		securityManager.setCacheManager(ehCacheManager());

		return securityManager;
	}

	/*
	 * 开启shiro aop注解支持 使用代理方式;所以需要开启代码支持;
	 */
	@Bean
	public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
			DefaultWebSecurityManager securityManager) {
		AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
		authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
		return authorizationAttributeSourceAdvisor;
	}

	/**
	 * DefaultAdvisorAutoProxyCreator,Spring的一个bean,由Advisor决定对哪些类的方法进行AOP代理。
	 */
	@Bean
	@ConditionalOnMissingBean
	public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
		DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
		defaultAAP.setProxyTargetClass(true);
		return defaultAAP;
	}

	/*
	 * shiro缓存管理器;
	 * 需要注入对应的其它的实体类中-->安全管理器:securityManager可见securityManager是整个shiro的核心;
	 */
	@Bean
	public EhCacheManager ehCacheManager() {
		System.out.println("ShiroConfiguration.getEhCacheManager()");
		EhCacheManager cacheManager = new EhCacheManager();
		cacheManager.setCacheManagerConfigFile("classpath:ehcache.xml");
		return cacheManager;
	}

}

realms(*)

     

package com.example.demo.realms;

import com.example.demo.service.UserService;
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.util.ByteSource;

import java.util.HashSet;
import java.util.Set;
/**
 * realm实现类,用于实现具体的验证和授权方法
 * @author Bean
 *
 */
public class MyShiroRealm extends AuthorizingRealm {

	/**
	 * 方面用于加密 参数:AuthenticationToken是从表单穿过来封装好的对象
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		System.out.println("doGetAuthenticationInfo:" + token);

		// 将AuthenticationToken强转为AuthenticationToken对象
		UsernamePasswordToken upToken = (UsernamePasswordToken) token;

		// 获得从表单传过来的用户名
		String username = upToken.getUsername();

		// 从数据库查看是否存在用户
		UserService userService = new UserService();

		// 如果用户不存在,抛此异常
		if (!userService.selectUsername(username)) {
			throw new UnknownAccountException("无此用户名!");
		}

		// 认证的实体信息,可以是username,也可以是用户的实体类对象,这里用的用户名
		Object principal = username;
		// 从数据库中查询的密码
		Object credentials = userService.selectPassword(username);
		// 颜值加密的颜,可以用用户名
		ByteSource credentialsSalt = ByteSource.Util.bytes(username);
		// 当前realm对象的名称,调用分类的getName()
		String realmName = this.getName();

		// 创建SimpleAuthenticationInfo对象,并且把username和password等信息封装到里面
		// 用户密码的比对是Shiro帮我们完成的
		SimpleAuthenticationInfo info = null;
		info = new SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName);
		return info;
	}

	// 用于授权
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

		System.out.println("MyShiroRealm的doGetAuthorizationInfo授权方法执行");

		// User user=(User)
		// principals.fromRealm(this.getClass().getName()).iterator().next();//获取session中的用户
		// System.out.println("在MyShiroRealm中AuthorizationInfo(授权)方法中从session中获取的user对象:"+user);

		// 从PrincipalCollection中获得用户信息
		Object principal = principals.getPrimaryPrincipal();
		System.out.println("ShiroRealm  AuthorizationInfo:" + principal.toString());

		// 根据用户名来查询数据库赋予用户角色,权限(查数据库)
		Set roles = new HashSet<>();
		Set permissions = new HashSet<>();
//		2018.09.14更新
		//		给用户添加user权限 (没有进行判断、对所有的用户给user权限)
		if("user".equals(principal)){
			roles.add("user");
			permissions.add("user:query");
		}
//		当用户名为admin时 为用户添加权限admin  两个admin可以理解为连个字段
		if ("admin".equals(principal)) {
			roles.add("admin");
			permissions.add("admin:query");
		}
//		为用户添加visit游客权限,在url中没有为visit权限,所以,所有的操作都没权限
		if("visit".equals(principal)){
			roles.add("visit");
			permissions.add("visit:query");
		}
//              更新以上代码
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);
		//添加权限
		info.setStringPermissions(permissions);
		return info;
		// return null;
	}

}
handler(controller)

           Ahandler

                    

package com.example.demo.handler;

import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class Ahandler {

	// 登录的url
	@RequestMapping({ "/login", "/" })
	public String indexHtml() {
		return "/index";
	}

	// 属于user角色@RequiresRoles("user")
	// 必须同时属于user和admin角@RequiresRoles({ "user", "admin" })
	// 属于user或者admin之一;修改logical为OR 即可@RequiresRoles(value = { "user", "admin"},
	// logical = Logical.OR)

	
	@RequestMapping("/showUserHtml.action")
	@RequiresRoles(value = { "user", "admin"},logical = Logical.OR)
	@RequiresPermissions("user:query")
	public String userHtml() {
		return "/user";
	}

	
	@RequestMapping("/showAdminHtml.action")
	@RequiresRoles("admin")
    @RequiresPermissions("admin:query")
	public String adminHtml() {
		
		return "/admin";
	}

	@RequestMapping("/unauthorized.action")
	public String unauthorized() {
		return "/abc";
	}

	@RequestMapping("/LoginSuccess.action")
	public String listHtml() {
		return "/list";
	}
	
	
	@RequestMapping("/error.action")
	public String error() {
		int a=1/0;
		return "/abc";
	}

}

       LoginHandler 

package com.example.demo.handler;

import com.example.demo.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpSession;
import java.util.Map;
/*
 * 登陆的controller
 */
@Controller
public class LoginHandler {

	@Autowired
	private UserService userService;

	@RequestMapping("/login.action")
	public String login(String username, String password, Map map, HttpSession session) {
		System.out.println(username + "---" + password);
		// 获得当前Subject
		Subject currentUser = SecurityUtils.getSubject();
		// 验证用户是否验证,即是否登录
		if (!currentUser.isAuthenticated()) {
			String msg = "";
			// 把用户名和密码封装为 UsernamePasswordToken 对象
			UsernamePasswordToken token = new UsernamePasswordToken(username, password);

			// remembermMe记住密码
			token.setRememberMe(true);
			try {
				// 执行登录.
				currentUser.login(token);

				// 登录成功...
				return "redirect:/LoginSuccess.action";
			} catch (IncorrectCredentialsException e) {
				msg = "登录密码错误";
				System.out.println("登录密码错误!!!" + e);
			} catch (ExcessiveAttemptsException e) {
				msg = "登录失败次数过多";
				System.out.println("登录失败次数过多!!!" + e);
			} catch (LockedAccountException e) {
				msg = "帐号已被锁定";
				System.out.println("帐号已被锁定!!!" + e);
			} catch (DisabledAccountException e) {
				msg = "帐号已被禁用";
				System.out.println("帐号已被禁用!!!" + e);
			} catch (ExpiredCredentialsException e) {
				msg = "帐号已过期";
				System.out.println("帐号已过期!!!" + e);
			} catch (UnknownAccountException e) {
				msg = "帐号不存在";
				System.out.println("帐号不存在!!!" + e);
			} catch (UnauthorizedException e) {
				msg = "您没有得到相应的授权!";
				System.out.println("您没有得到相应的授权!" + e);
			} catch (Exception e) {
				System.out.println("出错!!!" + e);
			}
			map.put("msg", msg);
			return "/index";
		}

		// 登录成功,重定向到LoginSuccess.action
		return "redirect:/LoginSuccess.action";

	}

}

service业务逻辑

package com.example.demo.service;

import java.util.ArrayList;
import java.util.List;

import org.springframework.stereotype.Service;

import com.example.demo.entity.User;
/**
 * User的service,这里面的数据时静态数据,不查询数据库了
 * @author lenovo
 *
 */
@Service
public class UserService {

	// 用户的集合
	private List users = new ArrayList<>();

	public UserService() {
		// 从数据库查出来的用户名,密码,这是是静态数据(密码是123456)
		users.add(new User("admin", "038bdaf98f2037b31f1e75b5b4c9b26e"));
		users.add(new User("user", "098d2c478e9c11555ce2823231e02ec1"));
	}

	// 判断是否用户名是否存在
	public boolean selectUsername(String username) {
		for (User user : users) {
			if (user.getUsername().equals(username)) {
				return true;
			}
		}
		return false;
	}

	// 根据用户返回查询的密码
	public String selectPassword(String username) {
		for (User user : users) {
			if (user.getUsername().equals(username)) {
				return user.getPassword();
			}
		}
		return "";

	}

}

 exception全局异常捕捉

package com.example.demo.exception;

import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * 全局异常捕捉类
 * 
 * @author Bean
 *
 */
@ControllerAdvice
public class AllException {

	/// 角色權权限异常捕捉
	@ExceptionHandler(value = UnauthorizedException.class)
	@ResponseBody // 在返回自定义相应类的情况下必须有,这是@ControllerAdvice注解的规定
	public String roleException(UnauthorizedException e) {
		System.out.println("---------------------->" + e);
		return "角色权限不够!!!";
		// return "/abc";
	}

	// 其它异常异常捕捉
	@ExceptionHandler(value = Exception.class)
	@ResponseBody // 在返回自定义相应类的情况下必须有,这是@ControllerAdvice注解的规定
	public String allException(Exception e) {
		System.out.println("---------------------->" + e);
		return "系統出现异常!!!";
	}

}

entity实体类

package com.example.demo.entity;
/**
 * 实体类 
 * @author Bean
 *
 */
public class User {

	private Integer id;// 主键
	private String username;// 用户名
	private String password;// 密码

	@Override
	public String toString() {
		return "User [id=" + id + ", username=" + username + ", password=" + password + "]";
	}

	/**
	 * @return the id
	 */
	public Integer getId() {
		return id;
	}

	/**
	 * @param id
	 *            the id to set
	 */
	public void setId(Integer id) {
		this.id = id;
	}

	/**
	 * @return the username
	 */
	public String getUsername() {
		return username;
	}

	/**
	 * @param username
	 *            the username to set
	 */
	public void setUsername(String username) {
		this.username = username;
	}

	/**
	 * @return the password
	 */
	public String getPassword() {
		return password;
	}

	/**
	 * @param password
	 *            the password to set
	 */
	public void setPassword(String password) {
		this.password = password;
	}

	public User(String username, String password) {

		this.username = username;
		this.password = password;
	}

	public User() {

	}

}

ehcache 进行缓存权限数据 配置



       
       
       
       
       
       
       
       
       

    
    


    
    

    

    
    

    
     -->

    


html代码

index.html  登陆页面





Insert title here








	

Login HTML

用户名:
密码:

list.html  登陆成功页





Insert title here


list HTML

user html admin html error 1/0 html 退出

abc.html





Insert title here


	

abc HTML

admin.html





Insert title here


admin HTML

user.html





Insert title here


user HTML

                                         以上就是所有demo代码了...

测试的话 

                 先使用admin登陆  密码123456  ,权限方面都能通过

                 再使用user登陆 密码123456    ,权限方面showAdminHtml.action没有访问权限

springboot+shiro实现登陆过滤、权限认证、shiro密码加密、ehcache缓存(附demo项目)_第2张图片

对应代码资源         https://download.csdn.net/download/qq_32786139/10661538

链接: https://pan.baidu.com/s/1wqXqxcMU-olevX27RPbHwg

提取码: vapf 复制这段内容后打开百度网盘手机App,操作更方便哦

本文密码加密算法

		//盐值用的用的是对用户名的加密(测试用的"lisi")
                ByteSource credentialsSalt01 = ByteSource.Util.bytes("lisi");
		Object salt = null;//盐值
		Object credential = "123456";//密码
		String hashAlgorithmName = "MD5";//加密方式
                //1024指的是加密的次数
		Object simpleHash = new SimpleHash(hashAlgorithmName, credential,
				credentialsSalt01, 1024);
		System.out.println("加密后的值----->" + simpleHash);

直接写个main方法测试就可以了

你可能感兴趣的:(spring,boot,shiro)