Apache Shiro 框架(用户登录认证,用户授权和用户注销)_LiPengBo666的博客-程序员宅基地

技术标签: Dream Home  

一.简介

 1.  权限框架主要有三种:

① 自己写权限框架
② Spring Security (使用复杂, 依赖 Spring )
Apache Shiro (更轻量, 使用更简单, 并不完全依赖 spring,可以独立使用 )

2.导入jar包(版本1.3.2)

二.用户登录功能实现.

1.配置web.xml

代码:

	<!--Shiro的Filter -->
	<filter>
		<!-- 去spring配置文件中寻找同名bean -->
		<filter-name>shiroFilter</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>shiroFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

2.配置applicationContext-shiro.xml(别忘了在applicationContext.xml中引入)

代码:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:task="http://www.springframework.org/schema/task"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
		http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
		http://www.springframework.org/schema/data/jpa 
		http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

	<!-- 配置Shiro核心Filter -->
	<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		<!-- 安全管理器 -->
		<property name="securityManager" ref="securityManager" />
		<!-- 未认证,跳转到哪个页面 -->
		<property name="loginUrl" value="/login.html" />
		<!-- 登录页面页面 -->
		<property name="successUrl" value="/index.html" />
		<!-- 认证后,没有权限跳转页面 -->
		<property name="unauthorizedUrl" value="/unauthorized.html" />
		<!-- shiro URL控制过滤器规则 -->
		<property name="filterChainDefinitions">
			<value>
				/login.html* = anon
				/user_login.action* = anon
				/validatecode.jsp* = anon
				/css/** = anon
				/js/** = anon
				/images/** =anon
				/services/** = anon
				/pages/base/courier.html* = perms[courier:list]
				/pages/base/area.html* = roles[base]
				/** =authc
			</value>
		</property>
	</bean>

	<!-- 安全管理器 -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="realm" ref="bosRealm" />
	</bean>
	<!-- 配置Realm -->
	<bean id="bosRealm" class="cn.itcast.bos.realm.BosRealm">
		<!-- 缓存区的名字 就是 ehcache.xml 自定义 cache的name -->
		<property name="authorizationCacheName" value="bos" />
	</bean>

	<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
</beans>

※-->过滤器:

     ①anon 未认证可以访问
     ②authc 认证后可以访问
     ③perms 需要特定权限才能访问
     ④roles 需要特定角色才能访问
     ⑤user 需要特定用户才能访问
     ⑥port 需要特定端口才能访问
     ⑦reset 根据指定 HTTP 请求访问才能访问

     ⑧perms[courier:list]:用于用户权限控制,后面会有写到.


3.页面代码.

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<meta name="viewport" content="width=device-width,innitial-scale=1">  
		<title>BOS管理系统 登陆页面</title>
		<script src="./js/jquery-1.8.3.js" type="text/javascript"></script>
		<link rel="stylesheet" type="text/css" href="css/bootstrap.css">
		<link rel="stylesheet" type="text/css" href="css/login.css">
	</head>
	
	<body>
		<div class="loginbox">
		<div class="loginnav">
			<nav class="navbar navbar-default">
				<div class="container">
					<div class="navbar-header">
						<a class="navbar-brand" href="#"><img src="images/logo.png"></a>
						<span class="logintitle">员工登录</span>
					</div>
				</div>
			</nav>
		</div>
		
		<section class="mainlogin">
			<div class="container">
				<div class="col-md-4 col-md-offset-7 logincontent">
					<h4>员工登录</h4>
					<form class="form-horizontal" id="loginform" name="loginform" method="post" action="user_login.action">
						<div class="form-group" id="idInputLine">
							<label for="inputPassword3" class="col-sm-3 control-label">账号</label>
							<div class="col-sm-8">
								<input id="loginform:idInput" type="text" name="username" class="form-control" placeholder="请输入手机号/邮箱/用户名">
							</div>
						</div>
						<div class="form-group" id="pwdInputLine">
							<label id="loginform:pwdInput" class="col-sm-3 control-label" >密码</label>
							<div class="col-sm-8">
								<input for="pwdInput" type="password" name="password" class="form-control" id="inputaccount" placeholder="请输入您的密码">
							</div>
						</div>
						<div class="form-group">
							<label for="inputvalidate" class="col-sm-3 control-label">验证码</label>
							<div class="col-sm-4">
								<input type="text" class="form-control" id="inputaccount" placeholder="请输入验证码">
							</div>
							<div class="col-sm-4">
								<img id="loginform:vCode" src="validatecode.jsp"  οnclick="javascript:document.getElementById('loginform:vCode'). src='validatecode.jsp?'+Math.random();" />
							</div>
						</div>
						<div class="form-group">
							
							<div class="col-sm-offset-3 col-sm-4">
								<input type="checkbox"><span class="size12"> 记住用户名</span>
							</div>
							<div class="col-sm-4">
								<a href="#"><span class="size12 forget">忘记密码</span></a>
							</div>
						</div>
						<div class="col-md-offset-3 col-md-8">
							<a href="javascript:$('#loginform').submit();" id="loginform:j_id19" name="loginform:j_id19"
								 class="btn btn-danger" >立即登录</a>
							
						</div>
					</form>
				</div>
			</div>
		</section>

		<footer class="clearfix">
			<div class="container">
			<p class="text-center">地址:北京市昌平区建材城西路金燕龙办公楼一层 邮编:100096 电话:400-618-4000 传真:010-82935100 </p>
<p  class="text-center">京ICP备08001421号京公网安备110108007702</p>
</div>
		</footer>
		</div>
	</body>
</html>

注意:红色的"action"的字段在过滤器中是"/user_login.action* = anon",意思就是这个action可以不用认证(没有登录)就可以访问.

4.UserAction提供"login"登录的方法

package cn.itcast.bos.web.action.base.system;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.ParentPackage;
import org.apache.struts2.convention.annotation.Result;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;

import cn.itcast.bos.system.User;

import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;

@ParentPackage("json-default")
@Namespace("/")
@Controller
@Scope("prototype")
public class UserAction extends ActionSupport implements ModelDriven<User> {

	private User user = new User();

	@Override
	public User getModel() {
		return user;
	}

	@Action(value = "user_login", results = {
			@Result(name = "login", type = "redirect", location = "login.html"),
			@Result(name = "success", type = "redirect", location = "index.html") })
	public String login() {
		//基于Shiro实现登录
		Subject subject = SecurityUtils.getSubject();
		
		//用户名和密码信息
		UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(),user.getPassword());
		
		try {
			subject.login(token);
			//登录成功,将用户信息保存到session当中
			return SUCCESS;
		} catch (AuthenticationException e) {
			//登录失败
			e.printStackTrace();
			return LOGIN;
		}
		
	}
	
	//用户推出登录
	@Action(value="user_logout",results={@Result(name="success",type="redirect",location="login.html")})
	public String logout(){
		Subject subject = SecurityUtils.getSubject();
		subject.logout();
		return SUCCESS;
	}
}

Shiro 执行流程: 应用程序 --- Subject(action) --- SecurityManager(xml) --- Realm 安全数据

5.自定义Realm对象(实际开发中,只需要继承 AuthorizingRealm )

①将自定义 Realm 注入安全管理器 SecurityManager 当中

<!-- 安全管理器 -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="realm" ref="bosRealm" />
	</bean>
②Realm

package cn.itcast.bos.realm;


import java.util.List;

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 org.springframework.stereotype.Service;

import cn.itcast.bos.service.base.system.PermissionService;
import cn.itcast.bos.service.base.system.RoleService;
import cn.itcast.bos.service.base.system.Userservice;
import cn.itcast.bos.system.Permission;
import cn.itcast.bos.system.Role;
import cn.itcast.bos.system.User;


// 自定义Realm ,实现安全数据 连接
 @Service("bosRealm")
public class BosRealm extends AuthorizingRealm {

	@Autowired
	private Userservice userService;
	
	@Autowired
	private RoleService roleService;

	@Autowired
	private PermissionService permissionService;


	
	@Override
	// 认证...
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken token) throws AuthenticationException {
		System.out.println("shiro 认证管理... ");

		// 转换token
		UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;

		// 根据用户名 查询 用户信息
		User user = userService.findByUsername(usernamePasswordToken
				.getUsername());
		if (user == null) {
			// 用户名不存在
			// 参数一: 期望登录后,保存在Subject中信息
			// 参数二: 如果返回为null 说明用户不存在,报用户名
			// 参数三 :realm名称
			return null;
		} else {
			// 用户名存在
			// 当返回用户密码时,securityManager安全管理器,自动比较返回密码和用户输入密码是否一致
			// 如果密码一致 登录成功, 如果密码不一致 报密码错误异常
			return new SimpleAuthenticationInfo(user, user.getPassword(),
					getName());
		}

	}
}
红色:错误异常请参考我的博客"最权威正解Submitted credentials for token [xxx]] did not match the expected credentials."这篇文章

③Service和Dao代码

package cn.itcast.bos.service.base.system;

import cn.itcast.bos.system.User;

public interface Userservice {

	public User findByUsername(String username);
}

package cn.itcast.bos.service.base.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import cn.itcast.bos.dao.base.system.UserRepository;
import cn.itcast.bos.service.base.system.Userservice;
import cn.itcast.bos.system.User;
@Service
@Transactional
public class UserServiceImpl implements Userservice{

	@Autowired
	private UserRepository userRepository;

	@Override
	public User findByUsername(String username) {
		 return userRepository.findByUsername(username);
	}
	
}

package cn.itcast.bos.dao.base.system;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import cn.itcast.bos.system.User;
@Repository
public interface UserRepository extends JpaRepository<User, Integer>{
	public User findByUsername(String username);
}

6.在user表中插入临时用户密码数据,可以校验代码是否正确;

三,用户授权

1.表之间的关系;

菜单,权限,角色,用户

①用户:每个用户属于一个角色


②每个角色有相应的权限,还有对应的菜单

总结:每一位用户都有对应的权限,也有属于自己的菜单列表(动态显示菜单,后面有

2.实现 Realm的授权方法

完整的Realm(包括认证和授权)的代码:

package cn.itcast.bos.realm;


import java.util.List;

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 org.springframework.stereotype.Service;

import cn.itcast.bos.service.base.system.PermissionService;
import cn.itcast.bos.service.base.system.RoleService;
import cn.itcast.bos.service.base.system.Userservice;
import cn.itcast.bos.system.Permission;
import cn.itcast.bos.system.Role;
import cn.itcast.bos.system.User;


// 自定义Realm ,实现安全数据 连接
 @Service("bosRealm")
public class BosRealm extends AuthorizingRealm {

	@Autowired
	private Userservice userService;	//用户
	
	@Autowired
	private RoleService roleService;//角色

	@Autowired
	private PermissionService permissionService;//权限名称


	
	@Override
	// 认证...
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken token) throws AuthenticationException {
		System.out.println("shiro 认证管理... ");

		// 转换token
		UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;

		// 根据用户名 查询 用户信息
		User user = userService.findByUsername(usernamePasswordToken
				.getUsername());
		if (user == null) {
			// 用户名不存在
			// 参数一: 期望登录后,保存在Subject中信息
			// 参数二: 如果返回为null 说明用户不存在,报用户名
			// 参数三 :realm名称
			return null;
		} else {
			// 用户名存在
			// 当返回用户密码时,securityManager安全管理器,自动比较返回密码和用户输入密码是否一致
			// 如果密码一致 登录成功, 如果密码不一致 报密码错误异常
			return new SimpleAuthenticationInfo(user, user.getPassword(),
					getName());
		}

	}


	@Override
	// 授权...
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) {
		System.out.println("shiro 授权管理...");
		SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
		// 根据当前登录用户 查询对应角色和权限
		Subject subject = SecurityUtils.getSubject();
		User user = (User) subject.getPrincipal();
		// 调用业务层,查询角色
		List<Role> roles = roleService.findByUser(user);
		for (Role role : roles) {
			authorizationInfo.addRole(role.getKeyword());
		}
		// 调用业务层,查询权限
		List<Permission> permissions = permissionService.findByUser(user);
		for (Permission permission : permissions) {
			authorizationInfo.addStringPermission(permission.getKeyword());
		}

		return authorizationInfo;
	}
}
授权:将用户对应的权限查询出

博主还没有弄懂查询用户权限之后返回到哪去了!!!知道的小伙请在下面留言,或者去哪个博客下看!

未完待续========================================================!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/LiPengBo666/article/details/78964643

智能推荐

靶形数独_denghuan6474的博客-程序员宅基地

小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他们想用数独来一比高低。但普通的数独对他们来说都过于简单了,于是他们向 Z 博士请教,Z 博士拿出了他最近发明的“靶形数独”,作为这两个孩子比试的题目。靶形数独的方格同普通数独一样,在99格宽×99格高的大九宫格中有99个33格宽×33格高的小九宫格(用粗黑色线隔开的)。在这个大九宫格中,有...

行业应对IDFA缺失各有方案 数盟方案更可靠有效_热点新视界的博客-程序员宅基地

随着苹果 iOS 14.5 正式版的推送,颠覆性的 ATT (App Tracking Transparency,应用跟踪透明度) 功能正式上线。随着这项重量级功能的推出,业内基于广告收入的企业无不担心苹果的这项隐私政策,因为 iOS 14.5 中的应用跟踪透明度功能给予了用户选择不允许应用或第三方应用跟踪的权利。这完全打破了互联网广告的传统玩法,业内一片慌张,急于寻求靠谱的解决方案。其中,在业内有着丰富风控经验的数字联盟,推出的idfa风控方案,非常受行业及媒体关注,受到了诸如《中国经营报》《21财经》

FRG图像文件格式(四):编码技术_weixin_34235105的博客-程序员宅基地

FRG图像文件格式(四):编码技术作者: [email protected]   2013.06.12 FRG是一种优化从磁盘加载和解码到显示需要的时间的图像文件格式. (更多介绍: http://blog.csdn.net/housisong/article/details/9077059 )这里介绍FRG涉及到的一些编码技术. Alpha通道的处理:   A...

maven的使用!!详细版_en ok的博客-程序员宅基地

maven的使用!!详细版本文转:http://blog.csdn.net/u010425776/article/details/52027706什么是Maven?如今我们构建一个项目需要用到很多第三方的类库,如写一个使用spring的Web项目就需要引入大量的jar包。一个项目Jar包的数量之多往往让我们瞠目结舌,并且Jar包之间的关系错综复杂,一个Jar包往往又会引用其他Jar包,缺少任...

SIKI学院:MySQL数据库从零到精通:七:课时 9 : 08-如何利用MySQL Workbench链接数据库_Smart_zy的博客-程序员宅基地

目录一.目的1.想:提高学习效率,所以将老师的内容记录下来二.参考1.SIKI学院三.注意1.课程资源下载1.MySQL下载地址四.操作:成功1.打开MySQL Workbench链接数据库的方法1.1 方法一:1.1 方法二:​一.目的1.想:提高学习效率,所以将老师的内容记录下来二.参考1.SIKI学院​​​​​​登录 - SiKi学院 - 生命不息,学习不止!正在上传…重新上传取消​http://www.sikiedu...

linux ubuntu安装diffbind包 bug解决_橙子榴莲巧克力的博客-程序员宅基地

这个包是目前安装出现bug最多的,记录一下bug解决方法,花了有10个小时=.=之前R装的3.4,出现很多问题,后来就直接安的最新的R4.0.3#先卸载老版本sudo apt remove r-base-coresudo passwd root#进入sudo vim /etc/apt/sources.list#编辑,因为我装的linux ubuntu 18.04所以对应的是bionicdeb https://cloud.r-project.org/bin/linux/ubuntu bioni

随便推点

PHP版本过高,出现Notice,Warning等警告的解决办法_chengxie1963的博客-程序员宅基地

因为php版本高,出现的问题类似如下: Notice:Undefinedindex:admin_nameinF:\wamp\www\XL\admin\index.phponline16 刚开始不以为然,只是在php文件中在这句话前边加上@标识符,感觉又很繁琐。但是最重...

java 信号和槽,Qt 的信号与槽(纯干货)_波多斯基的博客-程序员宅基地

接触Qt断断续续有些时间了,总看了一堆的文章说信号槽的概念,心里就想骂人,做为一个初学者,最重要的就是怎么写代码,写代码写多了,再去看理论,有时水到渠成的就明白那些理论了。但所有讲信号槽的都把一堆信号槽的好处说一通,把MFC的消息机制贬一通。具体代码应该怎么实现信号槽轻飘飘的就忽略过去了。直接开始吧!信号槽是为了实现两个对象之间的通信机制,说白了就是比如一个按钮对象点击了一下想告诉主界面并让主界面...

vmware安装macos时提示“客户机操作系统已禁用CPU,请关闭或重置虚拟机”问题_GSflyy的博客-程序员宅基地_macos客户机操作系统已禁用cpu请关闭或重置虚拟机

AMD电脑的VMware安装macOS 最后出现的错误,网上找了许多解决办法,最后依靠这个解决了我们需要在虚拟机运行之前打开虚拟机安装目录自动生成的macOS xxxx(你选择安装的版本号).vmx只需要在末尾添加cpuid.0.eax = "0000:0000:0000:0000:0000:0000:0000:1011"cpuid.0.ebx = "0111:0101:0110:1110:0110:0101:0100:0111"cpuid.0.ecx = "0110:1100:0

区块链_weixin_39080569的博客-程序员宅基地

区块链OpenSea 构建NTF智能合约(一)OpenSea生物样本合同合约部署和铸造生物示例代码是一个名为OpenSea Creatures的收藏品。OpenSea生物非常基础:它们各自具有独特的外观,特征和属性。先把示例代码down下来新建一个文件夹mkdie openSeaDemocd openSeaDemogit clone https://github.com/ProjectOpenSea/opensea-creatures.git我这里用的IDE是Intellij 。用I

1、Mysql无法创建外键的原因 2、MySql 外键约束 之CASCADE、SET NULL、RESTRICT、NO ACTION分析和作用..._bacanlu3823的博客-程序员宅基地

在Mysql中创建外键时,经常会遇到问题而失败,这是因为Mysql中还有很多细节需要我们去留意,我自己总结并查阅资料后列出了以下几种常见原因。1. 两个字段的类型或者大小不严格匹配。例如,如果一个是int(10),那么外键也必须设置成int(10),而不是int(11),也不能是tinyint。另外,你还必须确定两个字段是否一个为 signed,而另一个又是unsigned(即...

Router OS创建PPPOE服务器。_weixin_34054931的博客-程序员宅基地

点击菜单PPP,跳出PPP设置界面,这里就是设置PPPOE服务器的地方,下一步点击PPPoE Servers标签,点+跳出界面后配置服务器,具体的请看图解。下一步完成上面这点后,先做PPPOE服务器的IP地址池,地址池的作用就是下面的客户拨号时从池中自动获取IP地址,所以在这里你可以任意设置一个C类段,具体请看图解。下一步点击菜单PPP跳出设置PPP设置界面,点击标签Prof...