Shiro框架使用及配置
提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
根据权限管理系统引入,引出Shiro框架的配置和使用
一、权限管理系统
1.简介:
权限管理系统主要应用于系统的安全方面
1、前端 用户交互的系统 业务安全 没有漏洞的
例如对于邮件发送系统来说:要对.sh .bat .exe 后缀的文件进行参数校验以防止漏洞
2、后台 用户(管理员-非开发人员) 数据安全性
当具有这个权限的管理员才能进行这个相关的操作,吐过没有权限则不能进行操作和显示菜单
3、这时候后,为了管理这个就有了RBAC(Role-Based Access Control,基于角色的访问控制),就是同坐角色于权限进行关联,简单来说就是一个用户拥有多个角色,每一个角色拥有若干的权限,构造成“用户-角色-权限”的授权模型。在这中模型中,用户与角色之间,角色与权限之间,一般都是多对多的关系,一个管理系统拥有多个权限。大概需要5个表,用户表,角色表,权限表,用户-角色表,角色-权限表。
4、希望达到的终极目的:页面中的功能没有权限的不可见;在地址栏拼写请求时将其拦截。
5、我们可以使用拦截器或者过滤器实现,但是那些又会显得特别麻烦,这时候我们就需要了简化它,所以有了框架——Shiro
6、需要创建的表:
2.Shrio框架
它是属于apache的一个安全框架
subject:主题,可以使用户也可以是程序。主体要访问系统,系统需要对主题进行认证授权。
securityManger:安全管理器,主体进行认证和授权都是同各国securityManager进行。
authenticator:认证器,主体进行认证最红通过authenticator进行的。执行登录操作(认证)登录 用户名和密码
authorizeer:授权器,主题尽心授权最终通过authorizer进行的。获取这个身份下的所属权限,授权:当用用户进行认证后,根据这个用户信息获取权限列表;鉴权:当用户要执行操作时,进行鉴权
sessionManager:web应用中一般是用web容器对session进行管理,shiro页提供一台session管理的方式。
cacheManager:缓存管理器,主要对session和授权数据进行缓存,比如将授权数据通过cacheManager进行缓存管理,和cache整合对缓存数据进行管理。
作用:一个用户只有在登录后进行操作后才会查询数据库获取权限列表缓存,以后访问不会在访问,但是加入这个用户具备角色的管理,对自身拥有的角色进行了权限的更改后不会立即生效。
二、相关配置文件
spring-shiro.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:context="http://www.springframework.org/schema/context"
xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-4.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="/admin/login.jsp" />
<property name="successUrl" value="/admin/common/main" />
<property name="unauthorizedUrl" value="/admin/common/unauthorized" />
<property name="filterChainDefinitions">
<value>
/ = authc
/admin/logout.jsp = logout
/admin/news/list = perms["admin:news:list"]
/admin/news/add = perms["admin:news:add"]
/admin/news/update = perms["admin:news:edit"]
/admin/news/delete = perms["admin:news:delete"]
/admin/role/list = perms["admin:role:list"]
/admin/role/add = perms["admin:role:add"]
/admin/role/update = perms["admin:role:edit"]
/admin/role/delete = perms["admin:role:delete"]
/admin/admin/admin = perms["admin:admin:admin"]
/admin/** = authc
</value>
</property>
<property name="filters">
<map>
<entry key="authc" value-ref="authenticationFilter" />
<entry key="logout" value-ref="logoutFilter" />
</map>
</property>
</bean>
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManagerConfigFile" value="classpath:shiro-cache.xml"></property>
</bean>
<bean id="logoutFilter" class="org.apache.shiro.web.filter.authc.LogoutFilter">
<property name="redirectUrl" value="/admin/login.jsp" />
</bean>
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="authenticationRealm" />
<property name="cacheManager" ref="cacheManager"></property>
</bean>
<bean id="authenticationRealm" class="com.xja.shiro.AuthenticationRealm">
</bean>
<bean id="authenticationFilter" class="com.xja.filter.AuthenticationFilter" />
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager" />
<property name="arguments" ref="securityManager" />
</bean>
</beans>
AuthenticationRealm.java:
package com.xja.shiro;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import com.xja.pojo.Admin;
import com.xja.service.AdminService;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.pam.UnsupportedTokenException;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.InvalidDataAccessResourceUsageException;
import org.springframework.jdbc.JdbcUpdateAffectedIncorrectNumberOfRowsException;
public class AuthenticationRealm extends AuthorizingRealm{
@Autowired
private AdminService adminService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
Principal principal=(Principal) principals.fromRealm(getName()).iterator().next();
if(principal!=null) {
List<String> permissionURL = adminService.selectAdminPermissionById(principal.getId());
if(permissionURL!=null) {
SimpleAuthorizationInfo authorizationInfo=new SimpleAuthorizationInfo();
authorizationInfo.addStringPermissions(permissionURL);
return authorizationInfo;
}
}
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
AuthenticationToKen authenticationToKen=(AuthenticationToKen) token;
String loginName=authenticationToKen.getUsername();
String password=new String(authenticationToKen.getPassword());
System.out.println(loginName+" "+password);
if(!authenticationToKen.isValid()) {
throw new UnsupportedTokenException();
}
if(loginName!=null&&!"".equals(loginName)) {
Admin loginPOJO = adminService.selectAdminByLoginName(loginName);
System.out.println(loginPOJO);
if(loginPOJO==null) {
throw new UnknownAccountException();
}
if(!loginPOJO.getPwd().equals(password)) {
//有用户,信息错误 或者 用户锁定
Integer failcount = loginPOJO.getFailcount();
if(failcount<3){
loginPOJO.setFailcount(failcount+1);
loginPOJO.setLogindate(new Date());
adminService.updateAdmin(loginPOJO);
}else{
loginPOJO.setFailcount(0);
loginPOJO.setLogindate(new Date());
long currentTime = System.currentTimeMillis() ;
currentTime +=3*60*1000;
Date date=new Date(currentTime);
/*SimpleDateFormat dateFormat = new SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss");
System.out.println(dateFormat.format(date));*/
loginPOJO.setLockexpiredate(date);
adminService.updateAdmin(loginPOJO);
}
throw new UnknownAccountException();
}
Date newDate = new Date();
Date lockexpiredate = loginPOJO.getLockexpiredate();
System.out.println(!loginPOJO.getPwd().equals(password)+" "+newDate.before(lockexpiredate));
if(newDate.before(lockexpiredate)){
loginPOJO.setLogindate(new Date());
adminService.updateAdmin(loginPOJO);
throw new IncorrectCredentialsException();
}
System.out.println("成功");
//登录成功,更新用户信息
loginPOJO.setLogindate(new Date());
loginPOJO.setFailcount(0);
adminService.updateAdmin(loginPOJO);
return new SimpleAuthenticationInfo(new Principal(loginPOJO.getId(),loginPOJO.getLoginname()), password, getName());
}
return null;
}
}
AuthenticationToKen.java:
package com.xja.shiro;
import org.apache.shiro.authc.UsernamePasswordToken;
/**
* 自定义令牌
* @author zhaoran
2017年12月31日
*
*/
public class AuthenticationToKen extends UsernamePasswordToken{
private boolean isValid;
public AuthenticationToKen(String loginName,String password,boolean isValid) {
super(loginName, password);
this.isValid=isValid;
}
public boolean isValid() {
return isValid;
}
public void setValid(boolean isValid) {
this.isValid = isValid;
}
}
PrinciPal.java:
package com.xja.shiro;
import java.io.Serializable;
/**
*
*
* 身份牌
*
* 存储用户的 信息
* 当登录之后将用户的信息存放进这个类中
**/
public class Principal implements Serializable{
private Integer id;
private String loginName;
public Principal() {}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLoginName() {
return loginName;
}
public void setLoginName(String loginName) {
this.loginName = loginName;
}
public Principal(Integer id, String loginName) {
super();
this.id = id;
this.loginName = loginName;
}
}
AuthenticationFilter.java:
package com.xja.filter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import com.xja.shiro.AuthenticationToKen;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
public class AuthenticationFilter extends FormAuthenticationFilter{
@Override
protected boolean onAccessDenied(ServletRequest req, ServletResponse res) throws Exception {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String requestType = request.getHeader("X-Requested-With");
if (requestType != null && requestType.equalsIgnoreCase("XMLHttpRequest")) {
response.addHeader("loginStatus", "accessDenied");
response.sendError(HttpServletResponse.SC_FORBIDDEN);
return false;
}
return super.onAccessDenied(request, response);
}
@Override
protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
boolean bln=true;
String loginName=getUsername(request);
String password=getPassword(request);
HttpServletRequest req=(HttpServletRequest) request;
HttpSession session=req.getSession();
String captCode=request.getParameter("code");
String sessionCaptCode=(String) session.getAttribute("valcode");
if((null==sessionCaptCode)||(null==captCode)) {
bln=false;
}
if(sessionCaptCode!=null&&captCode!=null) {
if(!sessionCaptCode.equals(captCode)) {
bln=false;
}
}
return new AuthenticationToKen(loginName, password,bln);
}
@Override
protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request,
ServletResponse response) throws Exception {
WebUtils.issueRedirect(request, response, getSuccessUrl());
return false;
}
}
login.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://shiro.apache.org/tags" prefix="shiro" %>
<%@page import="org.apache.shiro.web.filter.authc.FormAuthenticationFilter"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<shiro:authenticated>
<%
response.sendRedirect(request.getContextPath()+"/admin/common/main");
%>
</shiro:authenticated>
<%
String loginError = (String) request.getAttribute(FormAuthenticationFilter.DEFAULT_ERROR_KEY_ATTRIBUTE_NAME);
if(loginError!=null){
if(loginError.equals("org.apache.shiro.authc.pam.UnsupportedTokenException")){
pageContext.setAttribute("loginError", "验证码错误");
}else if(loginError.equals("org.apache.shiro.authc.UnknownAccountException")){
pageContext.setAttribute("loginError", "用户名不存在或密码不正确");
}else if(loginError.equals("org.apache.shiro.authc.IncorrectCredentialsException")){
pageContext.setAttribute("loginError", "账号锁定中,暂时无法登录");
}
}
%>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" href="${root}/resources/css/pintuer.css">
<link rel="stylesheet" href="${root}/resources/css/admin.css">
<script src="${root}/resources/js/jquery.js"></script>
<script src="${root}/resources/js/pintuer.js"></script>
<title>管理员登录</title>
</head>
<body>
<div class="bg"></div>
<div class="container">
<div class="line bouncein">
<div class="xs6 xm4 xs3-move xm4-move">
<div style="height:150px;"></div>
<div class="media media-y margin-big-bottom">
</div>
<form action="login.jsp" method="post">
<div class="panel loginbox">
<div class="text-center margin-big padding-big-top"><h1>后台管理中心</h1></div>
<div class="panel-body" style="padding:30px; padding-bottom:10px; padding-top:10px;">
<div class="form-group">
<div class="field field-icon-right">
<input type="text" class="input input-big" name="username" placeholder="登录账号" data-validate="required:请填写账号" />
<span class="icon icon-user margin-small"></span>
</div>
</div>
<div class="form-group">
<div class="field field-icon-right">
<input type="password" class="input input-big" name="password" placeholder="登录密码" data-validate="required:请填写密码" />
<span class="icon icon-key margin-small"></span>
</div>
</div>
<div class="form-group">
<div class="field">
<input type="text" class="input input-big" name="code" placeholder="填写右侧的验证码" data-validate="required:请填写右侧的验证码" />
<img src="${root}/CaptServlet" alt="" width="100" height="32" class="passcode" style="height:43px;cursor:pointer;" onclick="this.src=this.src+'?'">
</div>
</div>
</div>
<span style="color: red;font-size: 12px;">${loginError}</span>
<div style="padding:30px;"><input type="submit" class="button button-block bg-main text-big input-big" value="登录"></div>
</div>
</form>
</div>
</div>
</div>
</body>
</html>
main.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://shiro.apache.org/tags" prefix="shiro" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>后台管理中心</title>
<link rel="stylesheet" href="${root}/resources/css/pintuer.css">
<link rel="stylesheet" href="${root}/resources/css/admin.css">
<script src="${root}/resources/js/jquery.js"></script>
</head>
<body>
<div class="header bg-main">
<div class="logo margin-big-left fadein-top">
<h1><img src="images/y.jpg" class="radius-circle rotate-hover" height="50" alt="" />后台管理中心</h1>
</div>
<div class="head-l"><a class="button button-little bg-green" href="http://127.0.0.1:8080/shop-web" target="_blank"><span class="icon-home"></span> 前台首页</a> <a href="##" class="button button-little bg-blue"><span class="icon-wrench"></span> 清除缓存</a> <a class="button button-little bg-red" href="../logout.jsp"><span class="icon-power-off"></span> 退出登录</a> </div>
</div>
<div class="leftnav">
<div class="leftnav-title"><strong><span class="icon-list"></span>菜单列表</strong></div>
<h2><span class="icon-user"></span>基本设置</h2>
<h2><span class="icon-pencil-square-o"></span>新闻管理</h2>
<ul>
<shiro:hasPermission name="admin:news:list">
<li><a href="${root}/admin/news/list" target="right"><span class="icon-caret-right"></span>浏览新闻</a></li>
</shiro:hasPermission>
<shiro:hasPermission name="admin:news:add">
<li><a href="${root}/admin/news/edit" target="right"><span class="icon-caret-right"></span>添加新闻</a></li>
</shiro:hasPermission>
</ul>
<h2><span class="icon-pencil-square-o"></span>系统管理</h2>
<ul>
<shiro:hasPermission name="admin:role:list">
<li><a href="${root}/admin/role/list" target="right"><span class="icon-caret-right"></span>浏览角色</a></li>
</shiro:hasPermission>
<shiro:hasPermission name="admin:role:add">
<li><a href="${root}/admin/role/toAdd" target="right"><span class="icon-caret-right"></span>添加角色</a></li>
</shiro:hasPermission>
</ul>
</div>
<script type="text/javascript">
$(function(){
$(".leftnav h2").click(function(){
$(this).next().slideToggle(200);
$(this).toggleClass("on");
})
$(".leftnav ul li a").click(function(){
$("#a_leader_txt").text($(this).text());
$(".leftnav ul li a").removeClass("on");
$(this).addClass("on");
})
});
</script>
<ul class="bread">
<li><a href="{:U('Index/info')}" target="right" class="icon-home"> 首页</a></li>
<li><a href="##" id="a_leader_txt">网站信息</a></li>
<li><b>当前语言:</b><span style="color:red;">中文</php></span>
切换语言:<a href="##">中文</a> <a href="##">英文</a> </li>
</ul>
<div class="admin">
<iframe scrolling="auto" rameborder="0" src="info.html" name="right" width="100%" height="100%"></iframe>
</div>
<div style="text-align:center;">
</div>
</body>
</html>