shiro基础及原理:http://kdboy.iteye.com/blog/1154644。强烈推荐非常好的文章,对shiro学习有很大帮助!
shiro例子:http://www.cnblogs.com/xql4j/archive/2013/03/30/2990920.html
跟我学shiro:http://jinnianshilongnian.iteye.com/blog/2049092
整合shiro大体步骤:
- web.xml的配置
- spring-shiro.xml配置
- realm实现类
- 认证&授权
一、web.xml
<!-- 配置Shiro过滤器,先让Shiro过滤系统接收到的请求 -->
<!-- 这里filter-name必须对应applicationContext.xml中定义的<bean id="shiroFilter"/> -->
<!-- 使用[/*]匹配所有请求,保证所有的可控请求都经过Shiro的过滤 -->
<!-- 通常会将此filter-mapping放置到最前面(即其他filter-mapping前面),以保证它是过滤器链中第一个起作用的 -->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<!-- 该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理 -->
<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配置文件
2.1 applicationContext.xml 或者spring-shiro.xml的配置 (这里只保留了shiro相关的信息)
<?xml version="1.0"encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.2.xsd" default-lazy-init="true"> <description>Shiro安全配置</description> <!-- shiro securityManager --> <!--Shiro默认会使用Servlet容器的Session, 可通过sessionMode属性来指定使用Shiro原生Session --> <!--即<property name="sessionMode"value="native"/>, 详细说明见官方文档 --> <!--这里主要是设置自定义的单Realm应用,若有多个Realm, 可使用'realms'属性代替 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="shiroDbRealm" /> <!-- <property name="cacheManager" ref="myShiroEhcacheManager" /> --> <!-- <property name="sessionMode" value="native"/> <property name="sessionManager" ref="sessionManager"/> --> </bean> <!-- 用户授权信息Cache,采用EhCache --> <bean id="myShiroEhcacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> <property name="cacheManagerConfigFile" value="classpath:ehcache-shiro.xml" /> </bean> <!--继承自AuthorizingRealm的自定义Realm, 即指定Shiro验证用户的认证和授权 --> <bean id="shiroDbRealm" class="org.shiro.demo.service.realm.ShiroDbRealm" depends-on="baseService"> <property name="userService" ref="userService" /> </bean> <!--Shiro主过滤器本身功能十分强大,其强大之处就在于它支持任何基于URL路径 表达式的、自定义的过滤器的执行 --> <!-- Shiro Filter --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <!-- Shiro的核心安全接口,这个属性是必须的 --> <property name="securityManager" ref="securityManager" /> <!--要求登录时的链接,非必须的属性, 默认会自动寻找Web工程根目录下的"/login.jsp"页面 --> <property name="loginUrl" value="/" /> <!-- 登录成功后要跳转的连接(本例中此属性用不到, 因为登录成功后的处理逻辑在LoginController里硬编码为main.jsp了) --> <property name="successUrl" value="/system/main" /> <!-- 用户访问未对其授权的资源时,所显示的连接 --> <property name="unauthorizedUrl" value="/system/error" /> <!-- Shiro过滤链的定义 --> <!-- 此处可配合这篇文章来理解各个过滤连的作用 http://blog.csdn.net/jadyer/article/details/12172839 --> <!--下面value值的第一个'/'代表的路径是相对于 HttpServletRequest.getContextPath()的值来的 --> <!--anon:它对应的过滤器里面是空的,什么都没做, 这里.do和.jsp后面的*表示参数,比方说login.jsp?main这种 --> <!-- authc:该过滤器下的页面必须验证后才能访问,它是Shiro内置的一个拦截器 org.apache.shiro.web.filter.authc.FormAuthenticationFilter --> <property name="filterChainDefinitions"> <value> /login = anon /validateCode = anon /** = authc </value> </property> </bean> <!--保证实现了Shiro内部lifecycle函数的bean执行; 起效权限注解,这个很少在web项目中用到,一般是控制url的访问,不是在controller中声明权限注解--> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" /> <!-- 开启Shiro的注解,实现对Controller的方法级权限检查(如 @RequiresRoles,@RequiresPermissions), 需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证 --> <!--配置以下两个bean即可实现此功能 --> <!-- Enable Shiro Annotations for Spring-configured beans. Only run after thelifecycleBeanProcessor has run --> <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>
2.2 applicationContext-mvc.xml配置
<aop:config proxy-target-class="true"></aop:config>
<!-- shiro注解方式必须放到mvc的文件中才会生效,不知道为什么 -->
<!--保证实现了Shiro内部lifecycle函数的bean执行; 起效权限注解,这个很少在web项目中用到,一般是控制url的访问,不是在controller中声明权限注解-->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
<!-- 开启Shiro的注解,实现对Controller的方法级权限检查(如 @RequiresRoles,@RequiresPermissions), 需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证 -->
<!--配置以下两个bean即可实现此功能 -->
<!-- Enable Shiro Annotations for Spring-configured beans. Only run after
thelifecycleBeanProcessor has run -->
<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>
三、重写Realm(认证与授权)
PS:
1、认证仅需要把原程序的身份认证过程移植到此处即可,操作简单。
2、授权,shiro三种方式实现授权:编码方式、注解方式、JSP TAGLIB
<pre name="code" class="java">package com.flf.controller;
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.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 org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.flf.entity.User;
import com.flf.service.UserService;
import com.flf.util.Const;
@Component
public class MyRealm extends AuthorizingRealm {
public Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private UserService userService;
public UserService getUserService() {
return userService;
}
public void setUserService(UserService userService) {
this.userService = userService;
}
/**
* 权限认证
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
// 因为非正常退出,即没有显式调用 SecurityUtils.getSubject().logout()
// (可能是关闭浏览器,或超时),但此时缓存依旧存在(principals),所以会自己跑到授权方法里。
if (!SecurityUtils.getSubject().isAuthenticated()) {
doClearCache(principal);
SecurityUtils.getSubject().logout();
return null;
}
User user = (User) SecurityUtils.getSubject().getSession().getAttribute(Const.SESSION_USER);
if(user != null){
//添加角色及权限信息
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addRole("admin");
info.addStringPermission("user:create");
//info.addRoles(roles);
//info.addStringPermissions(permissions)
return info;
}else{
return null;
}
}
/**
* 登录认证;
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken authcToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
System.out.println(token.getPassword()+ " "+token.getUsername());
String password = token.getPassword().toString();
// 调用业务方法
User user = userService.getUserByNameAndPwd(token.getUsername(),new String((char[])token.getCredentials()));
if(user!=null){
// 要放在作用域中的东西,请在这里进行操作
SecurityUtils.getSubject().getSession().setAttribute(Const.SESSION_USER,user);
System.out.println(SecurityUtils.getSubject().getPrincipal());
//info.setCredentialsSalt(ByteSource.Util.bytes(user.getUsername())) 密码加盐
//new SimpleAuthenticationInfo(user,user.getPassword().substring(16), ByteSource.Util.bytes(salt), getName());存储user对象,授权时方便取出
return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), ByteSource.Util.bytes("salt"), getName());//另一种加盐方式
// Cache<Object, Object> cache =
// shiroCacheManager.getCache(GlobalStatic.authenticationCacheName);
// cache.put(GlobalStatic.authenticationCacheName+"-"+userName,
// session.getId());
}
// 认证没有通过
return null;
}
/**
* 清空用户关联权限认证,待下次使用时重新加载。
*
* @param principal
*/
public void clearCachedAuthorizationInfo(String principal) {
SimplePrincipalCollection principals = new SimplePrincipalCollection(
principal, getName());
clearCachedAuthorizationInfo(principals);
}
}
注解方式需要springmvc也同时是controller注解或者开启aop拦截才会生效
2、基于注解的授权实现 | PS:引用自:http://kdboy.iteye.com/blog/1155450
Shiro注解支持AspectJ、Spring、Google-Guice等,可根据应用进行不同的配置。
相关的注解:
@ RequiresAuthentication
可以用户类/属性/方法,用于表明当前用户需是经过认证的用户。
- @RequiresAuthentication
- public void updateAccount(Account userAccount) {
- //this method will only be invoked by a
- //Subject that is guaranteed authenticated
- ...
- }
@ RequiresGuest
表明该用户需为”guest”用户
@ RequiresPermissions
当前用户需拥有制定权限
- @RequiresPermissions("account:create")
- public void createAccount(Account account) {
- //this method will only be invoked by a Subject
- //that is permitted to create an account
- ...
- }
@RequiresRoles
当前用户需拥有制定角色
@ RequiresUser
当前用户需为已认证用户或已记住用户