shiro大致介绍
shiro是什么
shiro是apache组织下维护的一个安全框架.
1. 帮我们做认证.
2. 帮我们做权限的校验.
3. 帮我们做密码加密加盐.
4. shiro可以自定义Session.
5. shiro可以帮我们缓存用户权限信息.
6. shiro还一共了各种认证/授权的校验方式,针对项目类型可以自由选择.
shiro和Spring security比较
- shiro可以脱离Spring单独使用. Spring security无法脱离Spring单独使用
- shiro是一款很轻的安全框架,学习简单. spring security相对更重,学习成本比较高.
- shiro关于全县控制的粒度相对更粗. spring security对权限的控制很细
- spring之前的官网用的是shiro.
shiro的架构
shiro的入门
创建项目
导入依赖
shiro-core-1.4.0
simpleRealm,iniRealm,JdbcRealm创建分为几步,大致相同
创建Realm对象
创建SecurityManager对象
2.1 给Security Manager对象设置创建好的Realm对象
生成Subject主题
3.1 给SecurityUtils设置上securityManager对象
3.2 用SecurityUtils创建subject主体
由主体发起认证请求 subject.login((“username”,“password”));
如果用户名或密码错误shiro直接抛出异常。
用户名错误UnKnowAccountException(账户异常)
密码错误IncorreCredentialsException(凭据异常|密码错误)
判断是否登陆过
subjec.isAuthentication
角色的授权认证
subject.checkRoles
角色的权限校验
subject.checkPremissions
simpleRealm
// 1. 创建SimpleRealm对象.
SimpleAccountRealm realm = new SimpleAccountRealm();
realm.addAccount("admin", "admin");
//创建创建SecurityManager对象
DefaultSecurityManager securityManager = new DefaultSecurityManager();
securityManager.setRealm(realm);
//创建Subject主题
SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
//4. 由主体发起认证请求.
subject.login(new UsernamePasswordToken("admin", "admin"));
// 如果用户名或密码错误,shiro直接抛出异常.
// 用户名错误 -> UnknowAccountException 密码错误 -> IncorrectCredentialsException
//判断是否验证过
if(subject.isAuthenticated()){
System.out.println("登陆成功");
}
//角色授权
boolean lala = subject.hasRole("lala");
System.out.println(lala);
iniRealm
shiro.ini
[users]
admin=admin,超级管理员,普通用户
[roles]
超级管理员=user:select,user:insert,user:delete,user:update
普通用户=user:select,user:insert
//创建iniRealmTest对象
IniRealm iniRealm = new IniRealm("classpath:shiro.ini");
//创建SecurityManager对象
DefaultSecurityManager securityManager = new DefaultSecurityManager();
securityManager.setRealm(iniRealm);
//创建subjec主体
SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
//由主体发送请求
subject.login(new UsernamePasswordToken("admin","admin"));
//判断是否校验过
if(subject.isAuthenticated()){
System.out.println("success");
}
//角色授权
subject.checkRoles("超级管理员","普通用户");
subject.checkPermissions("user:insert","user:delete");
jdbcRealm
//1.创建Realm
JdbcRealm jdbcRealm = new JdbcRealm();
// 1.1创建数据源
DruidDataSource ds=new DruidDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql:///shiro");
ds.setUsername("root");
ds.setPassword("123456");
//导入数据源(切记,老忘)
jdbcRealm.setDataSource(ds);
//1.2手动创建sql
jdbcRealm.setAuthenticationQuery("select password from user where username=?");
//手动开启权限校验
jdbcRealm.setPermissionsLookupEnabled(true);
//创建ScurityManager
DefaultSecurityManager securityManager = new DefaultSecurityManager();
securityManager.setRealm(jdbcRealm);
//创建subjec主体
SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
//主题提交认证请求
subject.login(new UsernamePasswordToken("admin","admin"));
if(subject.isAuthenticated()){
System.out.println("success");
}
//角色得授权
subject.checkRoles("普通用户");
//权限的授权
subject.checkPermissions("user:select");
CustomRealm自定义
-
自定义认证
//获取用户输入的用户名 String username = (String) token.getPrincipal(); //根据用户名查询用户信息(模拟数据库操作) User user = this.selectUserByUserName(username); if(user==null){ return null; } //4. 将正确的user对象和密码封装到AuthenticationInfo对象中. SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), "realName=DN"); //5. 将盐设置到info对象中 info.setCredentialsSalt(ByteSource.Util.bytes(user.getSalt())); return info;
实现密码的加密和密码的加盐. 密码加密: 在CustomRealm中,添加代码块,指定加密方式和加密次数. // 设置MD5加密1024次. { HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(); matcher.setHashAlgorithmName("MD5"); matcher.setHashIterations(1024); this.setCredentialsMatcher(matcher); } 在doGetAuthenticationInfo方法中,返回info对象之前.添加以下操作. info.setCredentialsSalt(ByteSource.Util.bytes(String salt));
-
自定义授权
//获取用户输入的用户名 User user = (User) principals.getPrimaryPrincipal(); String username = user.getUsername(); //根据用户名查询全部角色(模拟数据库操作) Set<String> allRoles = this.findAllRoleByUseName(username); //根据allRoles查询全部权限 Set<String> allPermissions = this.findAllPermissionsByUsername(allRoles); //4. 创建返回结果info,并封装角色和权限. SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(allRoles); info.setStringPermissions(allPermissions); return info;
shiro-Spring整合细节
- 创建项目
- 导入依赖
<!--spring-->
spring-context
spring-aspects
spring-jdbc
spring-webmvc
spring-test
<!--shiro整合spring-->
shiro-core
shiro-web
shiro-spring
<!--web-->
javax.servlet.jsp-api
javax.servlet-api
//记得添加限定<scope>provided</scope
<!--通用-->
junit
druid
mysql-connector-java
lombok
- web.xml配置
<!-- 处理post请求中文乱码问题-->
<filter>
<filter-name>characterRncoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter
</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterRncoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<--web.xml配置过滤器.(由当前过滤器映射全部请求,并将请求交给spring容器中的一个实例做处理)
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy
</filter-class>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listenerclass>org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<!-- 上下文参数-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext-*.xml</param-value>
</context-param>
<!-- 前端处理器-->
<servlet>
<servlet-name>springmvc</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>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
- 编写shiro整合spring的配置.
在shiro-spring.xml中配置id为shiroFilter的实例
注入Ralm
<bean id="realm" class="com.qf.realm.CustomRealm"></bean>
注入SecurityMannger
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="realm"/>
</bean>
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
过滤器链
<property name="filterChainDefinitions">
<value>
/user/logout=logout 退出登陆
/user/*=anon 直接放行
/**=authc 需要认证后放行
</value>
</property>
<property name="loginUrl" value="/user/login-ui"/>
logout -> 退出登录 -> subject.logout();
anon -> 直接放行 -> return true;
authc -> 认证后才放行 -> return subject.isAuthenticated();
</bean>
关于安全管理器
<bean id="realm" class="com.qf.realm.CustomRealm" />
<bean id="securityManager" class="DefaultWebSecurityManager">
<property name="realm" ref="realm" />
</bean>
测试
登录页面,需要认证访问的页面.
login-ui,login.
- 跳转到一个需要认证才可以进入的页面. -> 跳到登录页面.
- 执行登录.
- 跳转到一个需要认证才可以进入的页面. -> 直接进入.
- 退出登录. -> 跳转登录页面.
- 跳转到一个需要认证才可以进入的页面. -> 跳转登录页面.
问题1: 一定要使用listener去加载shiro-spring.xml文件.
web.xml中加载的顺序, listener > filter > servlet
如果采用servlet加载shiro-spring.xml会导致加载filter时,直接去spring容器中找bean的name为shiroFilter的实例,但是还没有加载servlet,导致启动报错.
问题2: 静态资源问题.
css,js,html,img
静态资源会被浏览器缓存,导致在退出登录后,依然可以访问静态资源.