1 Shiro简介
1.1 Shiro介绍
Apache Shiro是一个强大且易用的Java安全框架,有身份验证
、授权
、密码学
和会话管理
。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
Spring security 重量级
安全框架
Apache Shiro轻量级
安全框架
1.2 Shiro功能
Shiro 开发团队称为“应用程序的四大基石” ——身份验证,授权,会话管理和加密作为其目标
Authentication(身份认证):这是一个证明用户是他们所说的他们是谁的行为
Authorization(授权):访问控制的过程,也就是绝对“谁”去访问“什么”权限
Session Management:管理用户特定的会话,即使在非 Web 或 EJB 应用程序。
Cryptography:通过使用加密算法保持数据安全同时易于使用。
Web Support: Shiro 的 web 支持的 API 能够轻松地帮助保护 Web 应用程序。
Caching:缓存是 Apache Shiro 中的第一层公民,来确保安全操作快速而又高效。
Concurrency: Apache Shiro 利用它的并发特性来支持多线程应用程序。
Testing:测试支持的存在来帮助你编写单元测试和集成测试,并确保你的能够如预期的一样安全。
“Run As”:一个允许用户假设为另一个用户身份(如果允许)的功能,有时候在管理脚本很有用。
“Remember Me”:在会话中记住用户的身份,所以他们只需要在强制时候登录。
2 Shiro入门
2.1 配置shiro.ini模式
导包
<dependencies>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
测试 底层默认找IniRealm
2.2 自定义的Realm的模式
//自定义Realm
public class MyRealm extends AuthorizingRealm {
//授权
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//得到主体 --用户名
String primaryPrincipal = (String) principalCollection.getPrimaryPrincipal();
//根据用户名去拿到权限 -- 从数据库查询
Set<String> roles = getRoleByPrincipal(primaryPrincipal);
//根据用户名去拿到角色 - 从数据库查询
Set<String> permissions = getPermissionByPrincipal(primaryPrincipal);
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
//设置角色
simpleAuthorizationInfo.setRoles(roles);
//设置权限
simpleAuthorizationInfo.setStringPermissions(permissions);
//返回对象
return simpleAuthorizationInfo;
}
//通过用户拿到角色
private Set<String> getRoleByPrincipal(String primaryPrincipal) {
Set<String> roles = new HashSet<String>();
if("haoge".equals(primaryPrincipal)){
roles.add("admin");
return roles;
}else{
return null;
}
}
//通过用户拿到操作的权限
private Set<String> getPermissionByPrincipal(String primaryPrincipal) {
Set<String> permissions = new HashSet<String>();
if("haoge".equals(primaryPrincipal)){
permissions.add("driver:*");
return permissions;
}else{
return null;
}
}
//身份认证
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//得到令牌
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
//主体
Object principal = token.getPrincipal();
System.out.println(principal);
//获取用户名 zhangsan
String username = token.getUsername();
System.out.println(username);
//通过用户名 去数据库查询数据 -- 查询用户的信息
// 没有加密返回 String dbpwd= getPwdByUsername(username);
String md5Pwd = getMD5PwdByUsername(username);
//处理用户名问题 用户名是否存在的问题
if(md5Pwd==null){
//用户不存在
return null;
}
//ByteSource
ByteSource salt = ByteSource.Util.bytes("itsource");
//下面进行密码的比对 shiro 底层它会自动进行比对 -- 处理密码问题
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username, md5Pwd,salt, getName());
return simpleAuthenticationInfo;
}
//通过用户名去拿到密码
public String getPwdByUsername(String username){
if("haoge".equals(username)){
return "123";
}else{
return null;
}
}
//加密返回密码
public String getMD5PwdByUsername(String username){
if("haoge".equals(username)){
return "d5a3fedf6c59c2ecbe7f7a6c1a22da37";
}else{
return null;
}
}
public static void main(String[] args) {
//MD5 本身是不可破解 ..不可逆 网上看到破解方式 都是穷举把
//(1)普通加密+202cb962ac59075b964b07152d234b70 -123
SimpleHash simpleHash = new SimpleHash("MD5","123" );
//(2)加密+加次数
SimpleHash simpleHash1 = new SimpleHash("MD5","123",null,10 );
//5371007260db2b98e3f7402395c45f28
//(3)加密123 加盐 + itsource +加次数 --d5a3fedf6c59c2ecbe7f7a6c1a22da37 -- 最安全
SimpleHash simpleHash2 = new SimpleHash("MD5","123","itsource",10 );
System.out.println(simpleHash.toString());
System.out.println(simpleHash1.toString());
System.out.println(simpleHash2.toString());
}
}
测试
3 Shiro整合Spring
3.1 导入jar包
<!-- shiro的支持包 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>1.4.0</version>
<type>pom</type>
</dependency>
<!-- shiro与Spring的集成包 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
3.2 配置过滤器
通过代理过滤器 找shiro里面真实过滤器
<!--这是一个代理过滤器,它会到spring的配置中找一个名称相同的真实过滤器-->
<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>
3.3 配置shiro的 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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--配置核心的安全管理器-->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="myRealm" />
</bean>
<bean id="myRealm" class="cn.itsource.shiro.MyRealm">
<property name="credentialsMatcher">
<!--设置加密匹配方案-->
<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<!-- 编码的方式使用:md5 -->
<property name="hashAlgorithmName" value="MD5"/>
<!-- 编码的次数:10 -->
<property name="hashIterations" value="10"/>
</bean>
</property>
</bean>
<!-- 3.lifecycleBeanPostProcessor:可以自动调用在Spring Ioc窗口中 Shiro bean的生成周期方法 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<!-- 4.启动ioc容器中使用 shiro的注解,但是必需配置在Spring Ioc容器中Shiro bean的生成周期方法 -->
<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>
<!--真实过滤器 id必须是web.xml里面配的名字-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<!--认证没有成功跳转的页面-->
<property name="loginUrl" value="/s/login.jsp" />
<!--登录成功 进入的页面-->
<property name="successUrl" value="/s/main.jsp" />
<!--没有权限 进入的页面-->
<property name="unauthorizedUrl" value="/s/unauthorized.jsp" />
<!--
配置放行和拦截的请求
anon:不需要登录也可以访问相应的权限
authc:需要权限才能访问
/** :所有文件及其子文件
-->
<!--<property name="filterChainDefinitions">
<value>
/s/login.jsp=anon
/login=anon
<!–对资源进行权限控制,要控制的资源都应该从数据库查出,现在咱们先写死–>
/s/permission.jsp = perms[user:*]
/**=authc
</value>
</property>-->
<property name="filterChainDefinitionMap" ref="filterChainDefinitionMap" />
</bean>
<!--
配置bean
factory-bean:具体应用bean
factory-method:调用里面的方法
-->
<bean id="filterChainDefinitionMap" factory-bean="filterChainDefinitionMapBuilder" factory-method="createFilterChainDefinitionMap" />
<bean id="filterChainDefinitionMapBuilder" class="cn.itsource.shiro.FilterChainDefinitionMapBuilder" />
</beans>
3.4 注意:在启动服务的时候,引用配置文件
<!--引入配置文件-->
<import resource="ApplicationContext-shiro.xml" />
4 完成shiro的认证和授权
4.1 认证
前台:
准备登陆页面
后台:
创建Controller /LoginController
@Controller
public class LoginController {
@RequestMapping("/login")
public String login(String username,String pwd){
//主体
Subject subject = SecurityUtils.getSubject();
//判断该主体是否已经认证过
if (!subject.isAuthenticated()) {
try {
UsernamePasswordToken token = new UsernamePasswordToken(username, pwd);
subject.login(token);
}catch (UnknownAccountException uae) {
System.out.println("账号不存在");
} catch (IncorrectCredentialsException ice) {
System.out.println("密码有误");
} catch (LockedAccountException lae) {
System.out.println("这个账号已经被锁定");
}catch (AuthenticationException ae) {
System.out.println("其他认证错误");
}
}
return "redirect:/s/main.jsp";
}
}
4.2 登陆方法login(包含username,password)
4.3 身份认证
//身份认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String username = (String) authenticationToken.getPrincipal();
UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
String username1 = token.getUsername();
String pwd = getPwdByUsername(username1);
if (pwd == null){
return null;
}
//在这里加盐值需一个ByteSource对象,而Shiro提供了一个ByteSource对象给咱们
ByteSource salt = ByteSource.Util.bytes("itsource");
//密码比对
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username, pwd,salt,getName());
return authenticationInfo;
}
private String getPwdByUsername(String username){
if ("aaa".equals(username)){
return "d5a3fedf6c59c2ecbe7f7a6c1a22da37";
}else {
return null;
}
}
输入正确,完成登录,否则登录失败,返回登录界面
4.4 授权
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//当前用户的权限你弄出来 给 shiro
//得到主体用户
String username = (String) principalCollection.getPrimaryPrincipal();
//根据用户 从数据库拿到权限 加入到shiro里面去
Set<String> permissions = new HashSet<>();
if(username.equals("aaa")){
permissions.add("user:*");
permissions.add("employee:*");
}
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.addStringPermissions(permissions);
//返回权限里面 判断
return simpleAuthorizationInfo;
}
5 配置放行和拦截的请求,控制的资源从数据库查出
5.1 ApplicationContext-shiro.xml配置
<!--真实过滤器 id必须是web.xml里面配的名字-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<!--认证没有成功跳转的页面-->
<property name="loginUrl" value="/s/login.jsp" />
<!--登录成功 进入的页面-->
<property name="successUrl" value="/s/main.jsp" />
<!--没有权限 进入的页面-->
<property name="unauthorizedUrl" value="/s/unauthorized.jsp" />
<!--
配置放行和拦截的请求
anon:不需要登录也可以访问相应的权限
authc:需要权限才能访问
/** :所有文件及其子文件
-->
<!--<property name="filterChainDefinitions">
<value>
/s/login.jsp=anon
/login=anon
<!–对资源进行权限控制,要控制的资源都应该从数据库查出
/s/permission.jsp = perms[user:*]
/**=authc
</value>
</property>-->
<property name="filterChainDefinitionMap" ref="filterChainDefinitionMap" />
</bean>
<!--
配置bean
factory-bean:具体应用bean
factory-method:调用里面的方法
-->
<bean id="filterChainDefinitionMap" factory-bean="filterChainDefinitionMapBuilder" factory-method="createFilterChainDefinitionMap" />
<bean id="filterChainDefinitionMapBuilder" class="cn.itsource.shiro.FilterChainDefinitionMapBuilder" />
5.2 准备一个构造器类
//得到Map
public class FilterChainDefinitionMapBuilder {
public Map<String,String> createFilterChainDefinitionMap(){
Map<String, String> map = new LinkedHashMap();
map.put("/s/login.jsp","anon");
map.put("/login","anon");
//这个值之后从数据库中查询到【用户-角色-权限-资源】
map.put("/s/permission.jsp","perms[user:*]");
map.put("/**","authc");
return map;
}
}