权限控制器——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安全 框架的应用展示,如有不足敬请提出,大家一起进步。相关文章推荐
- thinkphp基础控制器的写法-待完善没有增加权限部分
- SpringMVC给控制器添加自定义注解控制访问权限
- ABP+AdminLTE+Bootstrap Table权限管理系统第六节--abp控制器扩展及json封装
- ASP.NET Zero--7.控制器加权限
- MVC基类控制器的会话丢失重新登录权限过滤
- SpringMVC给控制器添加自定义注解控制访问权限
- sharepoint视图权限控制器二次开发
- asp.net mvc 控制器&动作方法 角色 权限验证
- 通用后台管理系统(3)-编写权限接口、实现、控制器、界面类
- Yii-控制器- accessRules访问权限控制
- ThinkPHP:R()方法请求控制器无权限
- thinkphp5获取某个模块控制器的名称,用于菜单的权限控制
- 【无私分享:从入门到精通ASP.NET MVC】从0开始,一起搭框架、做项目(6) 控制器基类 主要做登录用户、权限认证、日志记录等工作
- (yii学习笔记)控制器权限控制
- PowerDesigner版本控制器设置权限
- ThinkPHP——RBAC父类控制器权限过滤(权限漏洞修复)
- 扫描spring mvc 控制器和方法,生成权限树
- 权限控制器AccessController
- SpringMVC给控制器添加自定义注解控制访问权限(未测试)
- thinkphp控制器权限 (在构造函数时统一加权限等)