您的位置:首页 > 其它

权限控制器——Shiro安全框架

2016-12-05 00:21 204 查看

【Shiro框架】

它是一种安全框架,用于解决系统认证和授权问题,同时提供了会化管理,数据加密机制。

传统的登录:



Shiro安全框架实现登




什么情况下使用Shiro框架:

用户登录时:检测用户是否登录正确、如登录错误或未登录状态、直接跳转到登录页面并给出提示。如果用户未登录直接访问后台,Shiro框架可以根据用户的请求给出相应的拦截, 并进行跳转到登录页面,防止用户未登录状态下访问到系统的核心功能区。假如用户登录信息认证通过,Shiro框架可以根据用户的不同角色身份,不同的权限进而系统中显示能够操作模块也不相同。这就是Shiro所谓的先认证后授权。

如何使用安全框架:



程序员只关注两部分:

1.如何获得Subject

2.如何定义一个符合规定的Realm域(密码比较器也是我们自己干的)

maven功能导包操作:

<!-- shiro -->
<!-- apache shiro dependencies -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>1.2.3</version>
</dependency>

用Shiro安全框架首先配置web.xml

<!-- Shiro Security filter  filter-name这个名字的值将来还会在spring中用到  一定要放到struts2核心过滤器之前 -->
<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>

产生代理类的方式:在applicationContext.xml中进行配置

<!-- Shiro产生代理的方式 :下面代码说明了使用cglib方式来生成代理-->
<aop:aspectj-autoproxy proxy-target-class="true"/>

<!--引入shiro.xml文件-->
<import resource="classpath:spring/applicationContext-shiro.xml"/>

配置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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> 
<description>Shiro的配置</description>
<!-- SecurityManager配置 -->
<!-- 配置Realm域 -->
<!-- 密码比较器 -->
<!-- 代理如何生成? 用工厂来生成Shiro的相关过滤器-->
<!-- 配置缓存:ehcache缓存 -->
<!-- 安全管理 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!-- Single realm app.  If you have multiple realms, use the 'realms' property instead. -->
<property name="realm" ref="authRealm"/><!-- 引用自定义的realm -->
<!-- 缓存 -->
<property name="cacheManager" ref="shiroEhcacheManager"/>
</bean>

<!-- 自定义权限认证 -->
<bean id="authRealm" class="cn.itcast.jk.shiro.AuthRealm">
<property name="userService" ref="userService"/>
<!-- 自定义密码加密算法  -->
<property name="credentialsMatcher" ref="passwordMatcher"/>
</bean>

<!-- 设置密码加密策略 md5hash -->
<bean id="passwordMatcher" class="cn.itcast.jk.shiro.CustomCredentialsMatcher"/>

<!-- filter-name这个名字的值来自于web.xml中filter的名字 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<!--登录页面  -->
<property name="loginUrl" value="/index.jsp"></property>
<!-- 登录成功后 -->
<property name="successUrl" value="/home.action"></property>
<property name="filterChainDefinitions">
<!-- /**代表下面的多级目录也过滤 -->
<value>
/index.jsp* = anon   <!--过滤器名-->
/home* = anon
/sysadmin/login/login.jsp* = anon
/sysadmin/login/logout.jsp* = anon
/login* = anon
/logout* = anon
/components/** = anon
/css/** = anon
/images/** = anon
/js/** = anon
/make/** = anon
/skin/** = anon
/stat/** = anon
/ufiles/** = anon
/validator/** = anon
/resource/** = anon
/** = authc
/*.* = authc  <!--过滤器-->
</value>
</property>
</bean>

<!-- 用户授权/认证信息Cache, 采用EhCache  缓存 -->
<bean id="shiroEhcacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManagerConfigFile" value="classpath:ehcache-shiro.xml"/>
</bean>

<!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

<!-- 生成代理,通过代理进行控制 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor">
<property name="proxyTargetClass" value="true"/>
</bean>

<!-- 安全管理器 -->
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
</beans>

Md5Hash加密工具类

package cn.itcast.jk.utils;

import org.apache.shiro.crypto.hash.Md5Hash;

/**
* @Description:
* @Author:		周家林
* @CreateDate:		2016年12月4日
*/
public class Encrypt {
/*
* 散列算法一般用于生成数据的摘要信息,是一种不可逆的算法,一般适合存储密码之类的数据,
* 常见的散列算法如MD5、SHA等。一般进行散列时最好提供一个salt(盐),比如加密密码“admin”,
* 产生的散列值是“21232f297a57a5a743894a0e4a801fc3”,
* 可以到一些md5解密网站很容易的通过散列值得到密码“admin”,
* 即如果直接对密码进行散列相对来说破解更容易,此时我们可以加一些只有系统知道的干扰数据,
* 如用户名和ID(即盐);这样散列的对象是“密码+用户名+ID”,这样生成的散列值相对来说更难破解。
*/

//高强度加密算法,不可逆
public static String md5(String password, String salt){
return new Md5Hash(password,salt,2).toString();
}

public static void main(String[] args) {
System.out.println(new Md5Hash("123456","tony",2).toString());
}
}


编写Realm域

package cn.itcast.jk.shiro;

import java.util.ArrayList;
import java.util.List;
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.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

import cn.itcast.jk.domain.Module;
import cn.itcast.jk.domain.Role;
import cn.itcast.jk.domain.User;
import cn.itcast.jk.service.UserService;

/**
* 自定义了一个Realm域 (主要作用是提供安全数据:用户,角色,模块)
* @author Administrator
*
*/
public class AuthRealm extends AuthorizingRealm {
//注入userService
private UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
//授权
/**
* 验证用户是否具有某某权限
*
* 当jsp页面上碰到Shiro标签时就会调用这个方法,当第一次碰到时才调用这个方法
*/
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) {
//1.得到用户信息
User user= (User) pc.fromRealm(this.getName()).iterator().next();
//2.通过对象导航得到用户的角色列表
Set<Role> roles = user.getRoles();
List<String> permissions = new ArrayList<String>();
//遍历角色列表,得到每个用户的角色
for(Role role:roles){
//得到每个角色,并通过对象导航,进一步加载这个角色下的模块
Set<Module> modules = role.getModules();
//遍历模块的集合,得到每个模块的信息
for(Module module:modules){
permissions.add(module.getName());
}
}
//声明AuthorizationInfo的一个子类对象
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermissions(permissions);

return info;
}
/**
* 认证(在登录时就会调用这个方法)    Subject.login();
* 参数:AuthenticationToken代表用户在界面上输入的用户名和密码
* 返回值不为null就会执行密码比较器
*/
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//1.将token转化为子类对象
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
//2.从token中获取用户界面输入的用户名
String username = upToken.getUsername();
//3.调用业务逻辑层,根据用户名查询用户对象
List<User> userList = userService.find("from User where userName=?", User.class, new String[]{username});
if(userList!=null && userList.size()>0){
//查询到了用户对象,说明了用户名是正确的
User user = userList.get(0);

//principal代表用户信息  credentials代表用户的密码         第三个参数:只要是一个字符串就可以
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,user.getPassword(),this.getName());
return info;
}
//4.组织返回的结果
return null;
}
}

已在applicationContext-shiro.xml中进行了配置。如下:

<!-- 自定义权限认证 -->
<bean id="authRealm" class="cn.itcast.jk.shiro.AuthRealm">
<property name="userService" ref="userService"/>
<!-- 自定义密码加密算法  -->
<property name="credentialsMatcher" ref="passwordMatcher"/>
</bean>

<!-- 设置密码加密策略 md5hash -->
<bean id="passwordMatcher" class="cn.itcast.jk.shiro.CustomCredentialsMatcher"/>


密码比较器CustomCredentialsMatcher

package cn.itcast.jk.shiro;

import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;

import cn.itcast.jk.utils.Encrypt;

/**
* 自定义的密码比较器
* @author 周家林
*
*/
public class CustomCredentialsMatcher  extends SimpleCredentialsMatcher {

/**
* 重写了密码比较的方法
*  第一个参数AuthenticationToken  代表用户在界面上输入的用户名和密码
*
*  第二个参数AuthenticationInfo   代表了当前这个用户在数据库中的信息,就会有加密后的密码
*
*  返回值:true证明密码比较成功了
*        false证明密码比较失败,密码输入错误,程序会抛出异常
*
*/
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
//1.将用户在界面输入的密码进行加密
UsernamePasswordToken upToken = (UsernamePasswordToken) token; //向下转型
String inputpwd = new String(upToken.getPassword());
String inputpwdEncrypt = Encrypt.md5(inputpwd, upToken.getUsername());//md5hash算法进行加密

//2.将用户在数据库中的密码读取出来
String dbPwd = info.getCredentials().toString();
//3.进行比较
return super.equals(inputpwdEncrypt, dbPwd);
}

}

LoginAction中的login和logout登录代码

//SSH传统登录方式
public String login() throws Exception {

//1.与Shiro交互
Subject subject = SecurityUtils.getSubject();
//如果已登录过,直接在页面上回车
if(subject.isAuthenticated()){
return SUCCESS;
}

if(UtilFuns.isEmpty(username)){
return "login";
}

try {
//2.调用subject中的方法,来实现登录
UsernamePasswordToken token = new UsernamePasswordToken(username, password);//将用户在界面输入的用户名密码进行封装
subject.login(token);//当login执行时,就会自动跳入authRealm域中的认证方法

//3.从Shiro中取出用户登录结果信息
User user = (User) subject.getPrincipal();

//4.将用户信息保存到session中
session.put(SysConstant.CURRENT_USER_INFO, user);

return SUCCESS;
} catch (Exception e) {
e.printStackTrace();
request.put("errorInfo", "登录失败,用户名或密码错误!");
return "login";
}
}

//退出
public String logout(){
session.remove(SysConstant.CURRENT_USER_INFO); //删除session
SecurityUtils.getSubject().logout();//Shiro中的登录方法,才会真实的将JSESSIONID删除
return "logout";
}

测试授权过程:

当jsp页面上出现Shiro标签时就会执行AuthRealm域中的授权方法

1.引入 Shiro标签

<%@taglib lib="http:shiro.apache.org/tags" prefix="shiro"%>

2.使用shiro标签进行授权判断

举例:

 <shiro:hasPermission name="系统首页">

    <span id="topmenu" onclick="toModule('home');">系统首页</span><span id="tm_separator"></span>

</shiro:hasPermission>

其实shiro标签就和<c:if></c:if>很像,如果此用户拥有此模块操作权,此模块就会显示,没有权限,此模块就不会显示。

小结:

这是我对Shiro安全 框架的应用展示,如有不足敬请提出,大家一起进步。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: