shiro框架整合进入spring的简要叙述

本记录旨在将shiro框架应用到javaee中,且与spring整合(其他方面也不会啊呵)


    第一步:导入架包,官方网址为:
http://shiro.apache.org/download.html
一般在javaee项目中只需要以下三个架包就可以了:
shiro-spring-1.2.2.jar;   shiro-web-1.2.2.jar;   shiro-core-1.2.2.jar
 
    第二步:编写配置文件
   

    1、在web.xml中进行修改

 

    1>、要在web.xml中添加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>


 

    
         注意:该过滤器要放在struts2过滤器的前面


        2>、同时shiro会有一个自己的配置文件(是不是必须的不清楚,现在只知道它必须有),也须要在web.xml中告知系统其位置,写法如下(举个例子,名字不一定,可以自己起;前一个是spring的配置文件):

       

       <context-param>
             <param-name>contextConfigLocation</param-name>
             <param-value>classpath:applicationContext.xml,classpath:spring-shiro.xml</param-value>
       </context-param>


        
        2、在shiro自己的配置文件(在这里是叫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:util="http://www.springframework.org/schema/util"
 xsi:schemaLocation="http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd   
    http://www.springframework.org/schema/util 
    http://www.springframework.org/schema/util/spring-util-3.0.xsd">
 <description>Shiro 配置</description>
 <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
  <property name="securityManager" ref="securityManager" />
  <property name="loginUrl" value="/login.jsp" />
  <property name="successUrl" value="/index.jsp" />
  <property name="unauthorizedUrl" value="/noLogin.jsp" />
  <property name="filterChainDefinitions">
   <value>
    <!-- 下面的语句是对资源及页面等进行不对处理的设置,一般anon就是不做限制,随意都可以请求到,authc说明请求该资源需要验证权限 -->
    /**/*.png = anon
    /**/*.jpg = anon
    /**/*.gif = anon
    /**/*.js = anon
    /**/*.css = anon
    /logout.jsp = logoutFilter
    /** = authc
   </value>
  </property>
 </bean>

 <!--自定义Realm 继承自AuthorizingRealm,很重要,在此类中指明了请求不同用户各自数据源的不同方法,这个东西shiro实在是自己办不到了,哈哈,还得咱还给它指定。 -->
 <bean id="myRealm" class="com.security.MyRealm"></bean>
 <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
  <!--设置自定义realm -->
  <property name="realm" ref="myRealm" />
 </bean>

 <!-- securityManager 
 <bean
  class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
  <property name="staticMethod"
   value="org.apache.shiro.SecurityUtils.setSecurityManager" />
  <property name="arguments" ref="securityManager" />
 </bean>
 -->

 <!-- Enable Shiro Annotations for Spring-configured beans. Only run after -->
 <!-- the lifecycleBeanProcessor has run: -->
 <!-- 打开shiro注解加权限的功能 ,除了用标签对页面元素进行权限控制之外,还可以用注解对action类及方法进行权限的控制-->
 <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
 <bean
  class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
  depends-on="lifecycleBeanPostProcessor" />
 <bean
  class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
  <property name="securityManager" ref="securityManager" />
 </bean>

</beans>



      
 
      第三步:编写一个顶顶重要的类Realm,此类用来向shiro指明访问数据源(数据存放的位置)中存放的权限信息的方法;一般继承AuthorizingRealm类,此类为抽象类,有两个方法必须来实现,一个是doGetAuthorizationInfo,另一个是doGetAuthenticationInfo,下面对此两个方法进行详细说明:


        1、protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken autheToken) throws AuthenticationException


        此方法用于对请求登录的用户进行身份验证,就是在用户向系统提交登录凭证(一般就是用户名与密码)时,该方法负责将用户提交的凭证(autheToken)与数据库中存储的进行比对,返回给shiro是否有该用户(AuthenticationInfo )的信息,shiro凭借此判断是否让其登录,例子在下面的myRealm.java中有(且配有较详细的说明哦!)。


        本方法的调用时机:在系统发送登录(身份认证)请求时shiro会调用此方法来得到用户信息。

 

        2、protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection ps)
       

        此方法用于在对用户的某一操作进行权限检测时向shiro权限控制系统提供当前登录用户是否有此权限的信息,在此方法中要完成查询(到你的数据库或任何你存放权限信息的数据源中去查)你的权限信息并返回给shiro足够信息的功能。

        本方法的调用时机:在用户进行每一项请求时都会去查查看他有没有这个权限,此时,shiro就会去调此方法来得到当前登录用户的确切的权限信息。(例子在下面myRealm.java中)
        
       3、    本类(myRealm.java)在shrio的配置文件(此例中是spring-shiro.xml)中已经给securityManager指明了。

        myRealm.java

       

package com.security;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

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.AuthorizationException;
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 com.pojo.security.Permission;
import com.pojo.security.Role;
import com.pojo.security.User;
import com.service.IUserService;

public class MyRealm extends AuthorizingRealm{

 @Autowired
 private IUserService userService;
 
 @Override
 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection ps) {
  
  Set<String> permissions = new HashSet<String>();
  Set<String> roles = new HashSet<String>();//保持为空,直接使用权限
  Collection<User> principalsList = ps.byType(User.class);
  if (principalsList.isEmpty()) {
   throw new AuthorizationException("Empty principals list!");
  }
  for(User u : principalsList) {
   User oneUser = userService.findById(u.getUsid());
   permissions.addAll(getAllPermissions(oneUser));
  }
  //角色好像就是一个中间的符号而已,或者说像堆权限集合的统一名称一样,在此处没什么用,给它个空的就好了,只要保证把所有的权限返回就ok了应该
  SimpleAuthorizationInfo anthInfo = new SimpleAuthorizationInfo(roles);
  //将Set<String>类型的权限信息封装后然后返回给shiro以供其使用,当然也可以返回Permission对象集合的权限信息好像,反正原则就是你得告诉shiro到底有些什么权限
  anthInfo.setStringPermissions(permissions);
  return anthInfo;
 }
 /**
  * 传入一个User对象,查出它的全部权限,以{@code Set<String>}形式返回
  */
 public Set<String> getAllPermissions(User user) {
  
  Set<String> permissions = new HashSet<String>();
  Role role = user.getRole();
  if(role == null) {
   return new HashSet<String>();
  }
  Set<Permission> pers = role.getPers();
  for(Permission p : pers) {
   permissions.add(p.getPerName());
  }
  return permissions;
 }

 @Override
 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken autheToken) throws AuthenticationException {

  //UsernamePasswordToken是AuthenticationToken的子类,此例中传入的就是用户名/密码,所以可以转换成功
  UsernamePasswordToken token = (UsernamePasswordToken)autheToken;
  //用请求登录的用户名/密码在数据库中查找,看是否有该用户存在(注:若密码是用MD5之类加密过的,则在此处也就用相同的方法对密码进行处理)
  String hql = "from User t where t.username=? and t.password=?";
  User queryUser = userService.findFirstByHql(hql, new Object[]{token.getUsername(),String.valueOf(token.getPassword())});
  //返回携带有登录用户信息的认证信息(AuthenticationInfo),simpleAuthenticationInfo中第一个参数是一个用户的任何标识符,可以是id,用户名等,它是Object的,在此是用的User对象
  //,第二个参数是凭证、证书之类的,是秘密的东西,一般就指密码了,第三个是该Realm的“名字”,因在一个认证体系中,可以有多个Realm,返回“名字”可能是要对多个Realm进行验证选择之类的用途吧!
  return new SimpleAuthenticationInfo(queryUser, queryUser.getPassword(), getName());
 }

}


      


    第四步:构建用户/权限认证存储体系(呵,我自己起的,其实就是设计存储用户/权限信息的数据库表结构了)

 

        在前面的myRealm类的两个方法中对User对象的操作已经有了,但这个User怎么设计呢,有没有什么特殊的呢,现在就来说一说,其实一点特殊也没有,普普通通的一个类而已,我在此写的是一个最简单的吧;

        一般就是User-Role-Permission(用户-角色-权限),三个表的关系是User-Role:多对一(想着以后搞成一对多);Role-Permission:多对多;Permission-Permission:一对多(多对一???);第三种权限对权限的关联我也说不清了,反正意思就是说一个权限可能是另一个权限的子权限,每个权限都还有自己的子权限,意会就好了。

        三个简单的例子如下:
        Usr.java :
        

package com.pojo.security;

import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

import org.hibernate.annotations.GenericGenerator;

@Entity @Table(name="sys_user")
public class User {

 @Id
 @GeneratedValue(generator = "system-uuid")
 @GenericGenerator(name = "system-uuid", strategy = "uuid")
 private String usid;
 private String username;
 private String password;
 
 @ManyToOne(fetch = FetchType.LAZY)
 @JoinColumn(name="rid")
 private Role role;
 
 public String getPassword() {
  return password;
 }
 public void setPassword(String password) {
  this.password = password;
 }
 public String getUsername() {
  return username;
 }
 public void setUsername(String username) {
  this.username = username;
 }
 public Role getRole() {
  return role;
 }
 public void setRole(Role role) {
  this.role = role;
 }
 public String getUsid() {
  return usid;
 }
 public void setUsid(String usid) {
  this.usid = usid;
 }
}



         

        Role.java:

       

package com.pojo.security;

import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;

import org.hibernate.annotations.GenericGenerator;

@Entity @Table(name="sys_role")
public class Role {

 @Id
 @GeneratedValue(generator = "system-uuid")
 @GenericGenerator(name = "system-uuid", strategy = "uuid")
 private String rid;
 private String roleName;
 
 //与权限关联
 @ManyToMany(cascade = CascadeType.PERSIST)
 @JoinTable(name = "sys_role_permission", joinColumns = { @JoinColumn(name = "rid") }, inverseJoinColumns = { @JoinColumn(name = "pid") })
 private Set<Permission> pers;
 
 public String getRoleName() {
  return roleName;
 }
 public void setRoleName(String roleName) {
  this.roleName = roleName;
 }
 public String getRid() {
  return rid;
 }
 public void setRid(String rid) {
  this.rid = rid;
 }
 public Set<Permission> getPers() {
  return pers;
 }
 public void setPers(Set<Permission> pers) {
  this.pers = pers;
 }
 
}


     

        Permission.java:

       

package com.pojo.security;

import java.util.List;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;

import org.hibernate.annotations.GenericGenerator;

@Entity @Table(name="sys_permission")
public class Permission {

 @Id
 @GeneratedValue(generator = "system-uuid")
 @GenericGenerator(name = "system-uuid", strategy = "uuid")
 private String pid;
 //权限名称
 private String perName;
 //权限描述
 private String perDec;
 //权限状态(现在好像没什么用)
 private String perStatus;
 
 //与角色关联
 @ManyToMany(mappedBy = "pers", cascade = CascadeType.MERGE)
 private Set<Role> roles;
 
 //上级节点
 @ManyToOne(fetch=FetchType.LAZY)
 private Permission parent;
 //子节点
 @OneToMany(mappedBy="parent", fetch=FetchType.LAZY, cascade = CascadeType.ALL)
 private List<Permission> children;
 public String getPid() {
  return pid;
 }
 public void setPid(String pid) {
  this.pid = pid;
 }
 public String getPerName() {
  return perName;
 }
 public void setPerName(String perName) {
  this.perName = perName;
 }
 public String getPerDec() {
  return perDec;
 }
 public void setPerDec(String perDec) {
  this.perDec = perDec;
 }
 public Permission getParent() {
  return parent;
 }
 public void setParent(Permission parent) {
  this.parent = parent;
 }
 public List<Permission> getChildren() {
  return children;
 }
 public void setChildren(List<Permission> children) {
  this.children = children;
 }
 public Set<Role> getRoles() {
  return roles;
 }
 public void setRoles(Set<Role> roles) {
  this.roles = roles;
 }
 public String getPerStatus() {
  return perStatus;
 }
 public void setPerStatus(String perStatus) {
  this.perStatus = perStatus;
 }
}



       
第五步:准备了这么多,最后当然是怎么用啦。

 

            使用当前来说主要分两种情境,一种是登录时的身份验证,另一种则是对用户的某一动作进行权限的认证

 

            1、登录验证

           

            以最常用的用户名/密码登录为例(其实我就只会这一种),不管你用什么方法(表单最常用了吧)将用户填写的用户名/密码信息传到后台java类(比如一个Action类)中,然后就开始写java代码就行了,写法大致如下示:
            
      

       UsernamePasswordToken token = new UsernamePasswordToken(username, password); 
       token.setRememberMe(true);//可有可无,看你需要不需要把用户记住,至于什么是记住网上有解释
            Subject currentUser = SecurityUtils.getSubject();//用SecurityUtils得到当前Subject
       try {
            currentUser.login(token);  
       } catch ( UnknownAccountException uae ) { ... 
       } catch ( IncorrectCredentialsException ice ) { ... 
       } catch ( LockedAccountException lae ) { ...
       } catch ( ExcessiveAttemptsException eae ) { ...
       } catch ( AuthenticationException ae ) {  
       }


 
           如上写你的类,用当前的Subject调用login方法就相当于是触发了shiro去进行登录身份认证,它会一路走到咱在myRealm中的doGetAuthenticationInfo方法中去,用咱提供的方法去验证该用户名/密码是否是合法的用户凭证,如果存在,则login方法会顺利执行完成(不会抛异常),若一旦有任何问题,shiro会抛出划分明细的各个异常,以提醒你是哪里出错了。
            特别说明:  网上好多教程写到这里就不再写了,这给我初学此框架时带来了很大困扰,因为我参考的一个工程中用到了shiro,但是我死活也找不到Subjec.login()方法的调用,这不科学啊!最后在我硬着头皮读了源码之后才发,原来...shiro是可以默认进行验证的,前提是你的登录表单得按照一定的格式来写,这个格式就是:<1>.表单中的用户名文本框的name值应该是username,密码文本框的name值应该是password;<2>.表单必须提交到login.jsp(即表单的action属性值为必须为“/login.jsp”)。你的登录表单只要如此写,一旦提交,shiro源码中会调用Subjec.login()的方法来进行登录验证的。


            2、 权限验证

 

            权限验证就是说登录用户可以看见什么,不可以看见什么,可以操作什么、不可以操作什么;现在我知道的是三个方面,一个是页面,用shiro标签将对应的页面元素“包”起来,如果有查看此元素的权限则正常显示,反之,则你什么也不会看到;二是在action中使用注解来对某一请求的类或方法添加权限验证;三是在代码中实现。


        <1>. 页面的写法看起来像下面这个样子:

 

         <shiro:hasPermission name="权限1">
            <a href="javascript:MenuSwitch('test1')"><font size="4"> 权限1 下的一个超连接</font></a><br />
         </shiro:hasPermission>
        (说明:当当前登录用户确实有“权限1”这一权限时,则用户可以看到这个超连接,反之则看不到;shiro标签应该可以用到所有的页面元素中,而且它并不只是有hasPermission,还有其他的标签用法与此同)

         在使用标签前当然得先导入标签了,它看起来应该是类似这样的:
        <%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags"/>


        <2>. 用注解对某一方添加权限验证如这个样子:


        @RequiresPermissions("account:create") //冒号前面是操作的实体,后面是动作
        还有一些@RequiresRoles、@ RequiresUser、@ RequiresAuthentication、@ RequiresGuest之类的, 具体的我也不是很清楚了,用的时候再研究吧!


        <3>. 在代码中直接进行“手动”验证:

       

        //基于字符串的

        if (currentUser.isPermitted("printer:print:laserjet4400n")) {  
            //show the Print button
        } else {
           //don't show the button?  Grey it out?
        } 


        //断言的方式(即如果有这个权限,此句会平稳的执行,不会有任何反映,若没权限,不知道会怎么.....,没试过!)

        currentUser.checkPermission("account:open");


    补充说明:

        1.shiro“号称”四大功能,上面只说了两项,另两项是会话管理(session)与加密,我都没用过,在此mark一下;

        2.通过简单的扩展,shiro验证可以支持验证码,也还未实施,mark;

        3.更多详尽的说明可以在《Apache_Shiro参考手册中文版.pdf》中找到;

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值