1,导入jar包
和spring整合中需要在spring的jar包基础上导入这一个jar包即可
shiro整合ssm所需jar包
web.xml配置(主要配置shiro的过滤器,其他的是spring配置)
<!-- spring配置文件 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- SpringMVC的配置文件 -->
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 添加shiro过滤器,用来拦截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>
<!-- -------------------------------------------- -->
这里整合需要把shiro相关的配置在spring.xml配置文件中配置
spring.xml配置
<context:component-scan base-package="com.org.shiro">
<!-- 排除扫描注解 -->
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 1.配置 securityManager管理器-->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!-- 缓存管理器 -->
<property name="cacheManager" ref="cacheManager"/>
<!-- 配置session的管理方式 -->
<!-- <property name="sessionMode" value="native"/> -->
<!-- 单个realm配置 -->
<!-- <property name="realm" ref="shiroRealm"/> -->
<!-- 多realm配置(需要权限操作的要写在securityManager) -->
<property name="realms">
<list>
<ref bean="shiroRealm" />
<!-- <bean class="com.org.shiro.realm.NewShiroRealm"></bean> -->
</list>
</property>
</bean>
<bean id="modularRealmAuthenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator">
<!-- 设置多个Realm的策略 -->
<property name="authenticationStrategy">
<bean class="org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy"></bean>
</property>
</bean>
<!-- 2.配置缓存管理器,可以设置缓存 -->
<!-- 2.1 添加ehcache的jar包和配置文件 -->
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<!-- <property name="cacheManager" ref="ehCacheManager"/> -->
<property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/>
</bean>
<!-- 3.配置实现了realm接口的bean -->
<bean id="shiroRealm" class="com.org.shiro.realm.ShiroRealm">
<property name="credentialsMatcher">
<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<!-- 指定使用MD5加密方式加密前台密码 -->
<property name="hashAlgorithmName" value="MD5" />
<!-- 指定使用MD5加密的次数 -->
<property name="hashIterations" value="3" />
</bean>
</property>
</bean>
<!-- 4.配置lifecycleBeanPostProcessor,可以自动调用配置在spring中的shrio对象生命周期方法。 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<!-- 5.启用IOC容器中的shiro注解,但必须在配置 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>
<!--
6.配置shiroFilter过滤器相关的属性。
6.1 id必须 和web.xml中配置的filter-name一致。
-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<property name="unauthorizedUrl" value="/unauthorized"/>
<property name="loginUrl" value="/login"/>
<!--
配置哪些页面需要被保护,以及访问该页面所需要的权限。
anon: 表示可以匿名访问。
authc:表示需要登录之后才可以访问。
-->
<property name="filterChainDefinitions">
<value>
/login = anon
/start.jsp = anon
/user = roleOrFilter["1,2,3"]<!-- 已url允许多种角色访问,需要继承AuthorizationFilter类,重写isAccessAllowed方法 -->
/admin = roles[3]
/logout = logout
/** = authc
</value>
<!--
url模式使用ant风格模式
Ant路径通配符支持?、*、**,注意通配符匹配不包括目录分隔符"/”
?:匹配一个字符,如/admin?将匹配/admin1,但是不匹配/admin或/admin/。
*:匹配另个或多个字符串,如/admin*将匹配/admin、/admin123,但是不匹配/admin/1。
**:匹配路径中的另个或多个路径,如/admin/**将匹配/admin/a或/admin/a/b。
-->
</property>
</bean>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="jdbc:mysql://localhost:3306/springmvc" />
<property name="username" value="root" />
<property name="password" value="157953" />
</bean>
<!-- Spring扫描所有的mapper文件 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 设置连接池对象 -->
<property name="dataSource" ref="dataSource" />
<!-- Mappper所在的包路径 -->
<property name="mapperLocations" value="classpath:com/org/shiro/beans/*.xml" />
<!-- 指定mybatis的配置文件 -->
<property name="configLocation" value="classpath:conf.xml"></property>
</bean>
<!-- Spring 扫描DAO包 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- DAO包所在的包路径 -->
<property name="basePackage" value="com.org.shiro.dao" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>
<bean id="roleOrFilter" class="com.org.shiro.realm.MyRolesAuthorizationFilter"/>
还有个springmvc的xml,和不需要改变什么在这就不指出了
java试例
这个是登录验证
@RequestMapping(value = "/login",method = RequestMethod.POST)
public String login(User user) {
//获取当前的Subject
Subject currentUser = SecurityUtils.getSubject();
System.out.println(user+"========");
// 测试当前用户是否已经被认证,即是否已经登录。
if (!currentUser.isAuthenticated()) {
// 把用户名和密码封装为UsernamePasswordToken对象
UsernamePasswordToken token
= new UsernamePasswordToken(user.getName(), user.getPass());
// 记住密码
token.setRememberMe(true);
try {
// 执行登录
currentUser.login(token); //实际上调用Realm中的doGetAuthenticationInfo方法
return "index";
}catch (UnknownAccountException uae) { //当前用户不存在
System.err.println("当前用户不存在"+token.getPrincipal());
} catch (IncorrectCredentialsException ice) { //账户存在,密码错误
System.err.println("账户存在,密码错误"+token.getPrincipal());
} catch (LockedAccountException lae) { //用户被锁定
System.err.println("用户被锁定"+token.getPrincipal());
}catch (AuthenticationException e) { // 其他异常,是其他异常的父类
e.printStackTrace();
}
}
return "login";
}
编写身份验证的realm类
//添加授权功能需要继承AuthorizingRealm,这类继承与AuthenticatingRealm
public class ShiroRealm extends AuthorizingRealm{
@Autowired
private UserService us;
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//①、把AuthenticationToken转换为UsernamePasswordToken。
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
//②、从UsernamePasswordToken中获取username
String username = upToken.getUsername();
//③、调用数据库的方法,从数据库中查询username对应的记录。
User user = us.login(username);
System.out.println(user);
//④、若用户不存在,则可以跑出UnknownAccountException异常。
if(user == null) {
throw new UnknownAccountException("用户不存在.");
}
//⑤、根据用户信息的情况,决定是否需要抛出其它AuthenticationException异常。
if(user.isLock()) {
throw new LockedAccountException("用户被锁定");
}
//⑥、根据用户的情况,来构建AuthenticationInfo对象并返回。
//AuthenticationInfo info = new SimpleAuthenticationInfo(user.getName(), user.getPass(), this.getName());
//生成加密的盐值
//ByteSource salt = ByteSource.Util.bytes("xxx");
//这里的密码要是数据库中加密之后的密码。
AuthenticationInfo info = new SimpleAuthenticationInfo(user.getName(), user.getPass(), null, getName());
return info;
}
//授权调用的方法
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//-1.获取登录的用户名
String s = (String) principals.getPrimaryPrincipal();
System.out.println(s);
//-2.通过登录的用户名获取其权限信息,可能用到数据库
User user = us.login(s);
Set<String> set = new HashSet<>();
set.add(user.getQualification().toString());
//-3.构建SimpleAuthorizationInfo ,并将set集合放入SimpleAuthorizationInfo之中
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(set);
//-返回 SimpleAuthorizationInfo对象
return info;
}
}
Shiro内置了很多默认的拦截器,比如身份验证、授权等相关的。默认拦截器可以参考DefaultFilter枚举中的属性。
身份验证相关的拦截器
授权相关的拦截器
关于授权规则
规则:资源标识符:操作:对象实例ID即对哪个资源的哪个实例可以进行什么操作.其默认支持通配符权限字符串,
“ : ” 表示资源/操作/实例的分割。
“,” 表示操作的分割。
“ * ” 表示任意资源/操作/实例。
多层次管理:
例如:user:query、user:edit
冒号是一个特殊字符,它用来分隔权限字符串的下一部件:第一部分是权限被操作的领域,第二部分是被执行的操作。
多个值:每个部件能够保护多个值。因此,除了授予用户user:query和user:edit权限外,也可以简单地授予他们一个:user:query, edit
还可以用* 号代替所有的值,如:user:* ,也可以写:*:query,表示某个用户在所有的领域都有query 的权限
实例级访问控制:
这种情况通常会使用三个部件:域、操作、被付诸实施的实例。如:user:edit:manager
也可以使用通配符来定义,如:user:edit:*、user:*:*、user:*:manager
部分省略通配符:缺少的部件意味着用户可以访问所有与之匹配的值,比如:user:edit等价于user:edit:*、user等价于user:*:*
注意:通配符只能从字符串的结尾处省略部件,也就是说user:edit并不等价于user:*:edit
关于shiro标签
Shiro提供了JSTL标签用于在JSP页面进行权限控制,如根据用户登录显示相应的页面按钮。
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
guest标签;用户没有登录显示相应的信息,即游客访问信息。
<shiro:guest>
欢迎游客访问,<a href="login">登录</a>
</shiro:guest>
user标签:用户已经认证/使用记住我登录后显示的相应信息。
<shiro:user>
欢迎[<shiro:principal />]登录,<a href="logout">注销</a>
</shiro:user>
authenticated标签:用户已经身份验证通过,即Subject.login登录成功,不是记住我登录的。
<shiro:authenticated>
用户[<shiro:principal />]已身份验证通过。
</shiro:authenticated>
notAuthenticated标签:用户未进行身份验证,即没有调用Subject.login进行登录,包括记住我自动登录的也属于未进行身份验证。
<shiro:notAuthenticated>
用户未身份验证。
</shiro:notAuthenticated>
pincipal标签:用户用户身份信息,默认调用Subject.getPrincipal()获取,如果AuthenticationInfo使用对象(User)作为参数,可以通过property来指定用户名的属性。
<shiro:user>
欢迎[<shiro:principal property="username"/>]登录,
<a href="logout">注销</a>
</shiro:user>
hasRole标签:如果当前Subject有角色,将显示body内容。
<shiro:hasRole name="admin">
用户[<shiro:principal property="username"/>]拥有角色admin
</shiro:hasRole>
hasAnyRoles标签:如果当前Subject有任意一个角色,将显示body内容。
<shiro:hasAnyRoles name="admin,user">
用户[<shiro:principal property="username"/>]拥有admin,user角色
</shiro:hasAnyRoles>
lacksRole:如果当前用户没有角色,将显示body体内容。
<shiro:lacksRole name="admin">
用户[<shiro:principal property="username"/>]没有admin角色。
</shiro:lacksRole>
hasPermission:如果当前Subject有权限,则显示body内容。
<shiro:hasPermission name="user:create">
用户[<shiro:principal property="username"/>]有user:create权限。
</shiro:hasPermission>
lacksPermission标签:如果当前Subject有没有权限,将显示body内容。
<shiro:hasPermission name="user:create">
用户[<shiro:principal property="username"/>]有user:create权限。
</shiro:hasPermission>
权限注解
@RequiresAuthentication:表示当前Subject已经通过login 进行了身份验证;即Subject. isAuthenticated() 返回true
@RequiresUser:表示当前Subject 已经身份验证或者通过记住我登录的。
@RequiresGuest:表示当前Subject没有身份验证或通过记住我登录过,即是游客身份。
@RequiresRoles(value={“admin”, “user”}, logical= Logical.AND):表示当前Subject 需要角色admin 和user
@RequiresPermissions(value={“user:a”, “user:b”}, logical= Logical.OR):表示当前Subject 需要权限user:a或user:b。
从数据库中初始化资源和权限
该配置要在springmvc中配置,然后自定义工厂类(若是从数据库或资源要去掉xml文件的配置 )
<!-- 通过bean工厂来创建filterChainDefinitionMap -->
<bean id="filterChainDefinitionMap" factory-bean="filterChainDefinitionMapFactory"
factory-method="builderFilterChainDefinition" />
<!-- 将factory添加到spring容器中 -->
<bean id="filterChainDefinitionMapFactory"
class="com.org.shiro.realm.FilterChainDefinitionMapFactory" />
自定义的工厂类
//创建一个工厂,用来创建一个LinkedHashMap集合。
public class FilterChainDefinitionMapFactory {
public LinkedHashMap<String, String> builderFilterChainDefinition(){
LinkedHashMap<String, String> map = new LinkedHashMap<>();
//将xml中配置的信息,通过键值对的方式添加到map中,
//实际开发中可以从数据库中读取配置信息。
map.put("/login", "anon");
map.put("/index.jsp", "anon");
map.put("/logout", "logout");
map.put("/admin", "roles[admin]");
map.put("/user", "roles[user]");
map.put("/**", "authc");
return map;
}
}
会话管理
shiro 的session和servlet的session功能一样
会话监听器
需要实现该接口