一、以下是你可以用 Apache Shiro所做的事情:
1. 验证用户
2. 对用户执行访问控制,如:
判断用户是否拥有角色admin。
判断用户是否拥有访问的权限
3. 在任何环境下使用 Session API。例如CS程序。
4. 可以使用多个用户数据源。例如一个是oracle用户库,另外一个是mysql用户库。
5. 单点登录(SSO)功能。
6. “Remember Me”服务 ,类似购物车的功能,shiro官方建议开启。
二、 基本架构
Subject :实体,代表当前用户,方便交互,实际功能逻辑是由SecurityManager实现
SecurityManager : 安全管理器,负责所有与安全相关的操作,是Shiro的核心,负责与Shiro的其他组件进行交互
Realm : Shiro从Realm获取安全数据(如用户,角色,权限),数据源,需要我们自己实现并提供给框架
Authenticator : 负责身份验证,提供接口,需要我们自己实现并提供给框架
Authorizer :负责权限验证,提供接口,需要我们自己实现并提供给框架
SessionManager 会话管理器,不仅仅可以在Web环境中使用,也可以在普通javaSE中使用
SessionDAO:所有会话的CRUD功能
CacheManager:缓存控制器,来管理用户,角色,权限等的缓存
Cryptography : 密码模块,提供了一些常见的加密组件用于加密和解密
三.maven依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>de.svenkubiak</groupId>
<artifactId>jBCrypt</artifactId>
<version>0.4.1</version>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.10.2</version>
</dependency>
四、Shiro的配置
1.配置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>
2.配置shiro 的和Spring的整合的文件
<?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/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- ========================================================= Shiro Core
Components - Not Spring Specific ========================================================= -->
<!-- Shiro's main business-tier object for web-enabled applications (use
DefaultSecurityManager instead when there is no web environment) -->
<!-- shiro所必须使用核心组件 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="cacheManager" ref="cacheManager" />
<!-- Single realm app. If you have multiple realms, use the 'realms' property
instead. -->
<!-- <property name="sessionMode" value="native"/> -->
<property name="realm" ref="jdbcRealm" />
</bean>
<!-- Let's use some enterprise caching support for better performance. You
can replace this with any enterprise caching framework implementation that
you like (Terracotta+Ehcache, Coherence, GigaSpaces, etc -->
<!-- 配置shiro所必须使用的缓存框架: shiro会默认使用ehcache 也可以根据自己的需要来进行替换(redis,mamcache....) -->
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<!-- Set a net.sf.ehcache.CacheManager instance here if you already have
one. If not, a new one will be creaed with a default config: <property name="cacheManager"
ref="ehCacheManager"/> -->
<!-- If you don't have a pre-built net.sf.ehcache.CacheManager instance
to inject, but you want a specific Ehcache configuration to be used, specify
that here. If you don't, a default will be used.: <property name="cacheManagerConfigFile"
value="classpath:some/path/to/ehcache.xml"/> -->
<!-- 指定shiro所需要的缓存(配置文件) -->
<property name="cacheManagerConfigFile" value="classpath:ehcache.xml" />
</bean>
<!-- Used by the SecurityManager to access security data (users, roles,
etc). Many other realm implementations can be used too (PropertiesRealm,
LdapRealm, etc. -->
<bean id="jdbcRealm" class="com.zzsxt.lee.shiro.realm.MyRealm3">
<!-- 指定密码的加密算法为MD5 -->
<property name="credentialsMatcher">
<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<!-- hashAlgorithmName:指定了shiro的加密方式,默认就是MD5,如果需要使用其他的加密方式,就必须自定义实现 -->
<property name="hashAlgorithmName" value="MD5"></property>
<!--指定使用MD5加密的次数 -->
<property name="hashIterations" value="1024"></property>
</bean>
</property>
</bean>
<!-- ========================================================= Shiro Spring-specific
integration ========================================================= -->
<!-- Post processor that automatically invokes init() and destroy() methods
for Spring-configured Shiro objects so you don't have to 1) specify an init-method
and destroy-method attributes for every bean definition and 2) even know
which Shiro objects require these methods to be called. -->
<!-- shiro交给spring的IOC容器进行托管 自动调用init以及destroy方法,不需要再自己重写 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
<!-- Enable Shiro Annotations for Spring-configured beans. Only run after
the lifecycleBeanProcessor has run: -->
<!-- 开启shiro自带的注解 -->
<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>
<!-- Secure Spring remoting: Ensure any Spring Remoting method invocations
can be associated with a Subject for security checks. -->
<bean id="secureRemoteInvocationExecutor"
class="org.apache.shiro.spring.remoting.SecureRemoteInvocationExecutor">
<property name="securityManager" ref="securityManager" />
</bean>
<!-- 今天的重点(配置文件)!!!!!!!!! 1.在shiro的配置文件中一旦配置了shiroFilter,<bean>的id一定要和web.xml中的<fileter-name>一一对应(大小写) -->
<!-- Define the Shiro Filter here (as a FactoryBean) instead of directly
in web.xml - web.xml uses the DelegatingFilterProxy to access this bean.
This allows us to wire things with more control as well utilize nice Spring
things such as PropertiesPlaceholderConfigurer and abstract beans or anything
else we might need: -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<!-- 登录的页面:登录的页面 -->
<property name="loginUrl" value="login.jsp" />
<!-- 登录成功的页面:用户登录成功后跳转的页面 一般情况下无需配置,因为shiro在登录成功后会默认自动跳转到项目的根路径 localhost:8080/zzsxt -->
<!-- <property name="successUrl" value="success.jsp"/> -->
<!-- 没有权限的页面:如果该用户没有权限访问则会跳转的页面 -->
<property name="unauthorizedUrl" value="unauthorized.jsp" />
<!-- The 'filters' property is not necessary since any declared javax.servlet.Filter
bean defined will be automatically acquired and available via its beanName
in chain definitions, but you can perform overrides or parent/child consolidated
configuration here if you like: -->
<!-- 2.filterChainDefinitions配置信息 anon:匿名访问(无需登录直接就可以访问) js,css,pictures,login.jsp...
authc:只有认证(登录)后才可以访问 提交订单 配置权限的时候,如果都为绝对路径则会被覆盖: /login.jsp = anon /login.jsp
= authc 但是如果使用通配符,则通配符不会覆盖全面的绝对路径: /login.jsp = anon /** = authc 如果什么都不配置的情况下,则默认为anon -->
<property name="filterChainDefinitions">
<value>
/login.jsp = anon
/login = anon
/custom_sources/** = anon
<!-- roles[] 拥有该角色才能访问匹配的路径 只有登录进来的用户拥有admin角色才可以访问userinfo这个页面 猜想: 如果在[]中写入两个角色,是这两个角色都被满足的时候才能访问还是
只需要满足其中一个角色就可以访问? 猜想答案: 只要满足其中的一个角色就可以正常访问 验证: 需要满足所有的角色才可以正常访问该路径 -->
<!-- /WEB-INF/view/userinfo.jsp = roles[user,admin] -->
/view/userinfo.jsp = anon
/** = authc
</value>
</property>
</bean>
<bean id="userService" class="com.zzsxt.shiro.service.UserService"></bean>
</beans>
3.写Realm里面写认证和授权
public class MyRealm extends AuthorizingRealm {
/**
* @description 认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 1.把AuthenticationToken强转为UsernamePasswordToken
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
// 2.通过UsernamePasswordToken获取用户的用户名
// upToken.getPrincipal()
String username = upToken.getUsername();
// 3.从数据库中查询该用户是否存在
User user = new User(1, "zhangsan", "b2793335f43645fd8e00c7d18e14e05f", 1);
if (!username.equals(user.getUsername())) {
throw new UnknownAccountException("该用户不存在的!");
}
// 4.如果存在则查看该用户是否被锁定
if (0 == user.getLock()) {
throw new LockedAccountException("用户被锁定");
}
// 5.如果一切都正常,匹配密码
// 第一个参数:用户名
// 第二个参数:从数据库中查询出的密码
// 第三个参数:盐值
// 第四个参数:getName()-->通过反射获取当前自定义的Realm对象(MyRealm3)-->通过该对象可以获取从前台传递的密码
AuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(),
ByteSource.Util.bytes("123"), getName());
// 6.返回AuthenticationInfo对象
// info:SimpleAuthenticationInfo的第一个参数
System.out.println(info);
return info;
}
/**
* @description 授权
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection collection) {
// 1.从PrincipalCollection中获取认证阶段传过来的info信息
// 在授权阶段通过PrincipalCollection获取到的参数类型取决于在认证阶段中SimpleAuthenticationInfo的第一个参数传递的类型
User user = (User) collection.getPrimaryPrincipal();
String username = user.getUsername();
// 2.通过username查询该用户拥有的角色
System.out.println("需要从数据库中查询");
// 角色
Set<String> roles = new HashSet<String>();
if ("zhangsan".equals(username)) {
roles.add("admin");
}
roles.add("user");
// SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);
SimpleAuthorizationInfo infos = new SimpleAuthorizationInfo();
infos.addRoles(roles);
// 权限
System.out.println("需要从数据库中查询");
List<String> permissions = new ArrayList<String>();
/*permissions.add("menu:delete");*/
infos.addStringPermissions(permissions);
return infos;
}
}
4.写登录的方法
@RequestMapping(value = "/login", method = RequestMethod.POST)
public String login(@RequestParam("username") String username, @RequestParam("password") String password) {
System.out.println("我是处理登录的方法,我被访问过!");
// 1.获取当前的Subject对象
Subject currentUser = SecurityUtils.getSubject();
if (!currentUser.isAuthenticated()) {
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try {
currentUser.login(token);
} catch (AuthenticationException ae) {
ae.getMessage();
}
}
return "redirect: view/userinfo.jsp";
}
<h1>Login Page</h1>
<form action="login" method="post">
Username:<input type="text" name="username" /> <br />
Password:<input type="password" name="password" /> <br />
<input type="submit" value="Submit" />
</form>
5.我们要访问的界面
<body>
<h1>User Info</h1>
<h1>
Welcome:
<%-- <shiro:principal property="username"></shiro:principal> --%>
</h1>
用户信息
<shiro:principal defaultValue=""></shiro:principal>
<ul>
游客模式
<shiro:guest>
<li>query</li>
</shiro:guest>
admin 角色
<shiro:hasRole name="admin">
<li>update</li>
拥有删除的权限
<shiro:hasPermission name="menu:delete">
<li>delete</li>
</shiro:hasPermission>
<li>insert</li>
</shiro:hasRole>
</ul>
</body>
6.还可以限制方法的调用
@RequiresPermissions({"menu:insert"})
public String testService() {
return "zhangsan";
}