shiro的使用:
基本的配置:
一:pom.xml引入依赖
<!-- 整合shiro 安全框架 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
二:web.xml文件中:
<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>
三:spring-mvc.xml中:
<aop:config proxy-target-class="true"></aop:config>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager" />
</bean>
四:shiro.xml中:
其中 <bean id="shiroUserRealm" class="cn.common.controller.myRealm">是需要自己定义的,路径为定义的myRealm的路径
这里需要填写的value为服务器提供的controller接口
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- shiro工厂bean配置 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- shiro的核心安全接口 -->
<property name="securityManager" ref="securityManager"/>
<!-- 要求登录时的连接,即如果没有登陆需要跳转的连接-->
<property name="loginUrl" value="/loginUI.do"></property>
<!-- 登录成功后要跳转的连接(此处已经在登录中处理了) -->
<!-- <property name="successUrl" value="/index.jsp"></property> -->
<!-- 访问未对其授权的资源时,要跳转的连接 -->
<property name="unauthorizedUrl" value="/loginUI.do"></property>
<!-- shiro连接约束配置 -->
<property name="filterChainDefinitions">
<value>
<!-- 对静态资源设置允许匿名访问 -->
/images/** = anon
/js/** = anon
/css/** = anon
/static/** = anon
/bootstrap/** = anon
/jquery/** = anon
<!-- 可匿名访问路径,例如:验证码、登录连接、退出连接等 -->
/loginUserNamePassword.do = anon
<!--权限路径的设置,roles[]为string,表示需要的权限。 -->
/student/** = roles[1]
/fath/**= roles[2]
/teacher/** = roles[3]
<!--用户退出登录的接口,后端不需要实现该接口,logout拦截到/api/logout的url后,就自动清除登录状态回到首页了-->
<!--因为在web.xml中设置的url-parttern是/api/*,随意只有api开头的url才会被拦截-->
/api/logout = logout
<!-- 其他访问路径-->
/** = authc
</value>
</property>
</bean>
<!-- 配置shiro安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="shiroUserRealm"></property>
</bean>
<!-- 自定义Realm -->
<bean id="shiroUserRealm" class="cn.common.controller.myRealm">
<!-- 配置凭证算法匹配器 -->
<property name="credentialsMatcher">
<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<property name="hashAlgorithmName" value="MD5"/>
<property name="hashIterations" value="2"/>
</bean>
</property>
</bean>
<!--Shiro生命周期处理器-->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
</beans>
功能一:
拦截功能
即访问任意资源都需要进行拦截,如果没有登陆则转到登陆界面
在shiro.xml中设置:
其中 /loginUserNamePassword.do = anon表示页面/映射可以在没有权限下访问,因为如果所有资源都被拦截,那么用户提交的login(username,password)也会被拦截,所以需要loginUserNamePassword.do进行放行从而进行登陆
<property name="loginUrl" value="/loginUI.do"></property>
<property name="filterChainDefinitions">
<value>
<!-- 可匿名访问路径,例如:验证码、登录连接、退出连接等 -->
/loginUserNamePassword.do = anon
<!-- 其他访问路径,也就是没有权限的访问其他路径,都会拦截-->
/** = authc
</value>
</property>
功能二:
登陆功能:
首先分析数据库层面,需要有username,password
并且shiro默认你存入在数据库中的password是加密处理后的,所以从数据库中的提取的数据需要进行解密
<property name="credentialsMatcher">
<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<property name="hashAlgorithmName" value="MD5"/>
<property name="hashIterations" value="2"/>
</bean>
</property>
加密算法配置在shiro.xml文件的shiroUserRealm内
表示使用MD5进行2加密
所以对存入数据库中的数据,需要进行加密后存储,方法如下:即将密码进行加密存入数据库
String password = "123456";//要加密的字符串
public static String salt = "wjw";//盐
Integer hashIterations = 2;//散列次数
Md5Hash md5 = new Md5Hash(password, salt, hashIterations);
student.setUsername("123456");
student.setPassword(md5.toString());
student.setAuthority("1");
studentService.insertStudent(student);
数据库层面完成后,具体加密解密和验证比对功能由Realm完成。
需要注意一点,用户输入的username, password。username相当于唯一凭证, 默认是unique
是需要自己定义的,路径为定义的myRealm的路径。
此为我们的登陆器,必须 extends AuthorizingRealm
有doGetAuthorizationInfo和doGetAuthenticationInfo需要我们重写
方法重写,但是在程序中不需要我们显示调用
一:doGetAuthenticationInfo
doGetAuthenticationInfo(AuthenticationToken token) :token包含我们的username和password。这个方法目的是进行验证
return new SimpleAuthenticationInfo(family.getUsername(), family.getPassword(), ByteSource.Util.bytes(InitDatabase.salt), getName());
SimpleAuthenticationInfo会传入username,password,salt进行判断账号对应的密码是否正确。
二:doGetAuthorizationInfo
此方法目的是权限管理,即获得这个username的权限
doGetAuthorizationInfo(PrincipalCollection principals):
principals就是我们传入的username=(String)principals.getPrimaryPrincipal()
此方法的目的是进行权限传递,返回权限Set< String>
return new SimpleAuthorizationInfo(studentService.getAuthorityByName((String) principals.getPrimaryPrincipal()));
完整代码如下:
public class myRealm extends AuthorizingRealm {
@Autowired
FamilyService familyService;
@Autowired
StudentService studentService;
@Autowired
TeacherService teacherService;
static int authority;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
if (principals == null) {
return null;
}
authority=IndexController.authority;
System.out.println("我的权限是"+authority);
System.out.println("得到的值是"+(String)principals.getPrimaryPrincipal());
System.out.println("数据库中的权限是"+studentService.getAuthorityByName((String) principals.getPrimaryPrincipal()));
//将用户角色信息传入SimpleAuthorizationInfo
if(authority==1)return new SimpleAuthorizationInfo(studentService.getAuthorityByName((String) principals.getPrimaryPrincipal()));
if(authority==2)return new SimpleAuthorizationInfo(familyService.getAuthorityByName((String) principals.getPrimaryPrincipal()));
if(authority==3)return new SimpleAuthorizationInfo(teacherService.getAuthorityByName((String) principals.getPrimaryPrincipal()));
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
authority=IndexController.authority;
if (token == null) {
return null;
}
if(authority==1){
Student student= studentService.getStudentByName((String) token.getPrincipal());
if(student==null)return null;
SecurityUtils.getSubject().getSession()
.setAttribute("currentUser",student);
return new SimpleAuthenticationInfo(
student.getUsername(), student.getPassword(), ByteSource.Util.bytes(InitDatabase.salt), getName());
}
if(authority==2){
Family family= familyService.getFamilyByName((String) token.getPrincipal());
if(family==null)return null;
SecurityUtils.getSubject().getSession()
.setAttribute("currentUser",family);
return new SimpleAuthenticationInfo(
family.getUsername(), family.getPassword(), ByteSource.Util.bytes(InitDatabase.salt), getName());
}
if(authority==3){
Teacher teacher= teacherService.getTeacherByName((String) token.getPrincipal());
if(teacher==null)return null;
SecurityUtils.getSubject().getSession()
.setAttribute("currentUser",teacher);
return new SimpleAuthenticationInfo(
teacher.getUsername(), teacher.getPassword(), ByteSource.Util.bytes(InitDatabase.salt), getName());
}
return null;
}
}
三:流程展示
我们将数据从 /loginUserNamePassword.do 登陆后,需要进行登陆即可。
其中subject.login(token)会调用doGetAuthenticationInfo方法
public void login(String username, String password) {
Subject subject = SecurityUtils.getSubject();
if(subject.isAuthenticated())throw new ServiceException("未授权用户");
UsernamePasswordToken token =
new UsernamePasswordToken(username, password);
try{//登录认证 - 调用userRealm
System.out.println("开始登陆");
subject.login(token);
}catch (IncorrectCredentialsException ice) {
throw new ServiceException("密码错误!");
} catch(AuthenticationException ae){
ae.printStackTrace();
throw new ServiceException("认证失败");
}
}
全局Session
当登陆正确后,可以利用将值封装在session中,之后前端页面直接调用${currentUser.username}就可以获得属性
SecurityUtils.getSubject().getSession().setAttribute("currentUser",teacher);
权限管理
1:首先,数据库中需要存储每一个user的不同权限
2:再次需要在shiro.xml进行权限设置,里面的意思就是如果访问/student/**的任意映射都需要拥有权限"1",否则会跳转到value="/loginUI.do"
<!-- 访问未对其授权的资源时,要跳转的连接 -->
<property name="unauthorizedUrl" value="/loginUI.do"></property>
<property name="filterChainDefinitions">
<value>
<!--权限路径的设置,roles[]为string,表示需要的权限。 -->
/student/** = roles[1]
/fath/**= roles[2]
/teacher/** = roles[3]
</property>
3:在实际访问过程中,当我们点击/student/user.do时,会执行Realm的doGetAuthorizationInfo方法,判断是否有权限