权限管理是一个系统不可缺少的一部分,也是比较复杂的一部分。
目前有二个主流权限管理的框架:shiro 和spring security ,以下讲解spring如何集成apache shiro
第一步:导入shiro的jar包
shiro-aspectj-1.2.2.jar
shiro-cas-1.2.2.jar
shiro-core-1.2.2.jar
shiro-ehcache-1.2.2.jar
shiro-guice-1.2.2.jar
shiro-quartz-1.2.2.jar
shiro-spring-1.2.2.jar
shiro-tools-hasher-1.2.2-cli.jar
shiro-web-1.2.2.jar
第二步:编写自己的Realm实现类:
package com.bs.common.shiro.realm;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import javax.annotation.Resource;
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.IncorrectCredentialsException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
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 com.bs.exception.BsException;
import com.bs.exception.BsExceptionCode;
import com.bs.system.po.BookUserInfo;
import com.bs.system.service.IBookUserService;
public class MyRealm extends AuthorizingRealm {
@Resource(name="bookUserService")
private IBookUserService bookUserService;
/**
* 授权
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
//arg0.getPrimaryPrincipal(): 实际上是在认证时返回的 SimpleAuthenticationInfo 的第一个参数!
// Object principal = arg0.getPrimaryPrincipal();
// ShiroUser user = (ShiroUser) principal;
ShiroUser user = (ShiroUser) SecurityUtils.getSubject().getSession().getAttribute("userObj");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.setRoles(user.getRoles());
info.setStringPermissions(user.getPermissions());
return info;
}
/**
* 认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken arg0) throws AuthenticationException {
//强转为UsernamePasswordToken类型
UsernamePasswordToken token=(UsernamePasswordToken)arg0;
//获取用户名和密码(密码要转为字符串类型)
String username = token.getUsername();
String password = new String(token.getPassword());
//测试一下,看是否得到了用户名和密码
System.out.println("username: " + username + ", password: " + password);
BookUserInfo bookUserInfo = null;
try {
bookUserInfo = bookUserService.loginService(username, password);
if(bookUserInfo==null){
throw new IncorrectCredentialsException("密码错误");
}
} catch(BsException bs){
if(BsExceptionCode.ERROR_CODE_NO_DATA==bs.getErrorCode()){
throw new UnknownAccountException("账户不存在,请联系管理员!");
}
}
//利用新建的类来创建对象
ShiroUser user=new ShiroUser();
user.setUsername(username); //将页面中的username值设置进去
//模拟设置权限部分
//实际项目中:从数据库根据用户名去查询其角色和权限
if("admin".equals(username)){
//如果用户名为:admin,则为其增加2个角色 admin和user
//这里是模拟设置角色
user.getRoles().add("admin");
user.getRoles().add("user");
Set<String> permissions = new HashSet<String>();
//这里是模拟设置权限
permissions.add("book:add");
permissions.add("book:create");
user.setPermissions(permissions);
}else {
//如果用户名不为admin,则为其增加user角色
user.getRoles().add("user");
}
SecurityUtils.getSubject().getSession().setAttribute("userObj", user);
return new SimpleAuthenticationInfo(username, password,getName());
}
@Override
public String getName() {
return "myRealm";
}
//新建一个类定义用户角色和权限
class ShiroUser implements Serializable{
private static final long serialVersionUID = 1L;
private String username;
private Set<String> roles= new HashSet<String>();
private Set<String> permissions = new HashSet<String>();
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Set<String> getRoles() {
return roles;
}
public void setRoles(Set<String> roles) {
this.roles = roles;
}
public Set<String> getPermissions() {
return permissions;
}
public void setPermissions(Set<String> permissions) {
this.permissions = permissions;
}
}
}
第三步:在src目录下新建applicationContext-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:tx="http://www.springframework.org/schema/tx"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!--2.配置CacheManager实例:管理Shiro相关缓存操作 -->
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManagerConfigFile" value="classpath:ehcache-shiro.xml"></property>
</bean>
<!--3.配置realm实例,实际的认证和授权都是由Realm实例来完成的! -->
<bean id="myRealm" class="com.bs.common.shiro.realm.MyRealm"></bean>
<!-- 4.配置 SecurityManager 实例. SecurityManager 是 Shiro 最核心的组件 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="cacheManager" ref="cacheManager"/>
<property name="realm" ref="myRealm"/>
</bean>
<!--5.配置bean的后置处理器来自动调用Shiro中的bean的init和destroy方法。 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"></bean>
<!--6.配置使shiro注解起作用的bean,需要放在 lifecycleBeanPostProcessor后面 -->
<aop:config proxy-target-class="true"></aop:config>
<!-- <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"></bean> -->
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"></property>
</bean>
<!--
7.配置哪些页面需要被拦截,以及访问这些页面所需的权限 。
该bean中的id 属性值必须和 web.xml 文件中配置的 filter 的 filter-name 值一致
-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"></property>
<!--①配置登陆页面 -->
<property name="loginUrl" value="/index.jsp"></property>
<property name="successUrl" value="/admin.jsp"></property>
<property name="unauthorizedUrl" value="/index.jsp"></property>
<!-- <property name="filters">
<util:map>
<entry key="authc">
<bean class="org.apache.shiro.web.filter.authc.PassThruAuthenticationFilter"/>
</entry>
</util:map>
</property> -->
<!--②配置需要被拦截的资源 以及访问权限 -->
<property name="filterChainDefinitions">
<value>
<!-- anon: 表示匿名的, 即任何人都可以访问 -->
/add.jsp=anon
/images/**=anon
/js/**=anon
/skin/**=anon
/upload/**=anon
/index.jsp=anon
/login.jsp=anon
/user/**=anon
/system/**=anon
/book/**=anon
/login=anon
/logout=logout
<!--③设置访问具体资源的权限 -->
/admin.jsp=roles[admin]
/user.jsp=roles[user]
<!-- authc 表示必须经过认证之后才可以访问的页面 -->
/**=authc
</value>
</property>
</bean>
</beans>
第四步:在src目录新建ehcache-shiro.xml配置文件,内容如下:
<ehcache>
<diskStore path="java.io.tmpdir/shiro-spring-sample"/>
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="false"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
/>
<cache name="shiro-activeSessionCache"
maxElementsInMemory="10000"
eternal="true"
overflowToDisk="true"
diskPersistent="true"
diskExpiryThreadIntervalSeconds="600"/>
<cache name="org.apache.shiro.realm.SimpleAccountRealm.authorization"
maxElementsInMemory="100"
eternal="false"
timeToLiveSeconds="600"
overflowToDisk="false"/>
</ehcache>
第五步:修改web.xml配置文件:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml,classpath:applicationContext-shiro.xml</param-value>
</context-param>
配置shiro过滤器:需要配置在struts2或者springmvc框架的拦截配置之前
<!--配置shiro过滤器 -->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
至此,spring集成shiro配置完毕。
编写登陆的处理方法:
public String login(){
//数据验证
if(ObjectFormatUtil.isNotNull(username) && ObjectFormatUtil.isNotNull(password)){
try {
//1.获取当前的用户
Subject currentUser = SecurityUtils.getSubject();
//2.把登录信息封装为一个 UsernamePasswordToken 对象
UsernamePasswordToken token=new UsernamePasswordToken(this.username,this.password);
currentUser.login(token);
} catch (UnknownAccountException uae) {
System.out.println("用户名不存在: " + uae);
return "input";
} catch (IncorrectCredentialsException ice) {
System.out.println("用户名存在,但密码和用户名不匹配: " + ice);
return "input";
} catch (LockedAccountException lae) {
System.out.println("用户被锁定: " + lae);
return "input";
} catch (AuthenticationException ae) {
System.out.println("其他异常: " + ae);
return "input";
}
}else{
message = "用户名或密码不能为空";
}
return "main";
}
编程退出登录方法:
public String loginout(){
//移除session
Subject currentUser = SecurityUtils.getSubject();
currentUser.logout();
return "loginPage";
}
shiro标签的使用:
以下配置表示:拥有admin角色的用户可以显示图书管理和系统管理菜单
<%@ taglib uri="http://shiro.apache.org/tags" prefix="shiro" %>
<shiro:hasRole name="admin">
<dd><div class='item' id='item8' onMouseMove="mv(this,'m',8);" onMouseOut="mv(this,'o',8);"><a href="${ctx}/jsp/main/user/bookmenu.jsp" οnclick="changeSel(8)" target="menu">图书管理</a></div></dd>
<dd><div class='item' id='item4' onMouseMove="mv(this,'m',4);" onMouseOut="mv(this,'o',4);"><a href="${ctx}/jsp/main/user/usermenu.jsp" οnclick="changeSel(4)" target="menu">系统管理</a></div></dd>
</shiro:hasRole>
使用注解配置action类中方法访问的权限:
以下注解表示拥有admin角色的用户可以访问该方法:
/**
* 添加分馆
* @return
*/
@RequiresRoles({"admin"})
public String doAdd(){
try {
if(!ObjectFormatUtil.isNotNull(bookLib.getLibname())){
message = "输入的信息有误";
return MESSAGE;
}
if(null==bookLibService.save(bookLib)){
message = "保存信息失败";
return MESSAGE;
}
return list();
} catch (Exception e) {
e.printStackTrace();
}
return "message";
}
当没有权限访问以下action类的方法时,会抛出org.apache.shiro.authz.UnauthorizedException异常,我们需要在struts2中进行该异常处理:
<global-results>
<result name="permissionmessage">/jsp/common/permissionmessage.jsp</result>
</global-results>
<global-exception-mappings>
<exception-mapping result="permissionmessage" exception="org.apache.shiro.authz.UnauthorizedException">
</exception-mapping>
</global-exception-mappings>
如果使用ajax异步访问我们action类中方法时,如果没有权限,我们该怎样处理了?
如:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c"%>
<c:set var="ctx" value="${pageContext.request.contextPath}"></c:set>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>add page</title>
<script type="text/javascript" src="${ctx}/js/jquery-2.1.1.min.js"></script>
<script type="text/javascript">
function add(){
$.ajax({
url:"${ctx}/system/testBookLib.action",
type:"post",
data:{"bookLib.libname":$("#libname").val()},
dataType:"json",
success:function(data){
if(data.result){
alert("添加成功");
}else{
alert(data.message);
}
}
});
}
</script>
</head>
<body>
图书馆名称:<input type="text" name="libname" id="libname"/> <br>
<input type="button" value="添加" id="btn_add" οnclick="add()"/>
</body>
</html>
1、我们可以使用以下方式:
public void test1(){
boolean result = false;
Subject subject = SecurityUtils.getSubject();
if(subject.hasRole("admin")){
//1验证数据
if(!ObjectFormatUtil.isNotNull(bookLib.getLibname())){
message = "输入的信息有误";
}
//2验证图书名称是否已存在 如果已存在 则添加失败 给出提示 反正添加成功
if(bookLibService.isExistBooklibName(bookLib.getLibname())){
if(null==bookLibService.save(bookLib)){
message = "保存信息失败";
}else{
result = true;
}
}else{
message = "图书名称已存在";
}
}else{
message ="无权访问";
}
outJsonString(response, "{\"result\":"+result+",\"message\":\""+message+"\"}");
}