shiro的使用心得

本文详细介绍了Apache Shiro的使用,包括在项目中的配置步骤、拦截功能实现、登录功能的处理以及数据库中密码的加密解密。重点讨论了如何设置权限规则,确保只有具备特定权限的用户才能访问特定资源,并展示了全局Session的运用,允许在登录成功后通过Session获取用户信息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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方法,判断是否有权限

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值