SSM整合Shiro进行登陆认证和授权详细配置

SSM整合Shiro进行登陆认证和授权详细配置

本篇博客将进行详细介绍Shiro+Spring+SpringMVC+Mybatis+数据库整合并进行登陆认证和授权详细配置。

SSM的整合可以参考:https://blog.csdn.net/a745233700/article/details/81049763

下面主要介绍Shiro与SSM的整合和使用:

1、导入Shiro需要的maven依赖:

		<!-- shiro -->
		<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-all -->
		<dependency>  
		        <groupId>org.apache.shiro</groupId>  
		        <artifactId>shiro-core</artifactId>  
		        <version>1.2.3</version>  
		    </dependency>
		    <dependency>
		        <groupId>org.apache.shiro</groupId>
		        <artifactId>shiro-ehcache</artifactId>
		        <version>1.2.3</version>
		    </dependency>
		    <dependency>
		        <groupId>org.apache.shiro</groupId>
		        <artifactId>shiro-web</artifactId>
		        <version>1.2.3</version>
		    </dependency>
		    <dependency>
		        <groupId>org.apache.shiro</groupId>
		        <artifactId>shiro-spring</artifactId>
		        <version>1.2.3</version>
		    </dependency>

或者一次性导入shiro的所有依赖:

<dependency>
	<groupId>org.apache.shiro</groupId>
	<artifactId>shiro-all</artifactId>
	<version>1.2.3</version>
</dependency>

2、在web.xml文件中配置shiro的filter拦截器:

在与Spring整合中,shiro也通过Filter进行拦截,但是拦截后的操作权交给spring中配置的filterChainDefinitions(过滤链)处理。

        <!-- shiro的filter -->
	<!-- shiro过虑器,DelegatingFilterProxy通过代理模式将spring容器中的bean和filter关联起来 -->
	<filter>
		<filter-name>shiroFilter</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
		<!-- 设置true由servlet容器控制filter的生命周期 -->
		<init-param>
			<param-name>targetFilterLifecycle</param-name>
			<param-value>true</param-value>
		</init-param>
		<!-- 设置spring容器filter的bean id,如果不设置则找与filter-name一致的bean-->
		<init-param>
			<param-name>targetBeanName</param-name>
			<param-value>shiroFilter</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>shiroFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

3、配置Shiro框架的相关配置:(applicationContext-shiro.xml文件中)

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
	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-4.0.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
	http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">

	<!-- id属性值要对应 web.xml中shiro的filter对应的bean -->
	 <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		<property name="securityManager" ref="securityManager"></property>
		<!-- loginUrl认证提交地址,如果没有认证将会请求此地址进行认证,请求地址将由formAuthenticationFilter进行表单认证 -->
		<property name="loginUrl" value="/login.action"></property>
		<!-- 认证成功统一跳转到first.action,建议不配置,shiro认证成功会默认跳转到上一个请求路径 -->
		<!-- <property name="successUrl" value="/first.action"></property> -->
		<!-- 通过unauthorizedUrl指定没有权限操作时跳转页面,这个位置会拦截不到,下面有给出解决方法 -->
		<!-- <property name="unauthorizedUrl" value="/refuse.jsp"></property> -->
		
		<!-- 过滤器定义,从上到下执行,一般将/**放在最下面 -->
 		<property name="filterChainDefinitions">
			<value>
				<!-- 对静态资源设置匿名访问 -->
				/images/** = anon
				/js/** = anon
				/styles/** = anon
				/validatecode.jsp =anon
				<!-- 请求logout.action地址,shiro去清除session -->
				/logout.action = logout
				<!-- /**=authc 所有的url都必须通过认证才可以访问 -->
				/** = authc
				
				<!-- /**=anon 所有的url都可以匿名访问,不能配置在最后一排,不然所有的请求都不会拦截 -->
			</value>
		</property>
	</bean>
	
	<!-- 解决shiro配置的没有权限访问时,unauthorizedUrl不跳转到指定路径的问题 -->
	<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <property name="exceptionMappings">
            <props>
                <prop key="org.apache.shiro.authz.UnauthorizedException">/refuse</prop>
            </props>
        </property>
    </bean>
	
	<!-- securityManager安全管理器 -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="realm" ref="customRealm"></property>
	</bean>
	
	<!-- 配置自定义Realm -->
	<bean id="customRealm" class="com.zwp.shiro.CustomRealm">
		<!-- 将凭证匹配器设置到realm中,realm按照凭证匹配器的要求进行散列 -->
		<property name="credentialsMatcher" ref="credentialsMatcher"></property>
	</bean>

	<!-- 凭证匹配器 -->
	<bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
		<!-- 加密算法 -->
		<property name="hashAlgorithmName" value="md5"></property>
		<!-- 迭代次数 -->
		<property name="hashIterations" value="1"></property>
	</bean>
</beans>

4、配置自定义的Realm,重写认证的方法:

//自定义的Realm
public class CustomRealm extends AuthorizingRealm{

	//注入service
	@Autowired
	private SysService sysService;
	// 设置realm的名称
	@Override
	public void setName(String name) {
		super.setName("customRealm");
	}
	
	//认证的方法
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		// token是用户输入的用户名和密码
		// 第一步从token中取出用户名
		String userCode = (String) token.getPrincipal();

		// 第二步:根据用户输入的userCode从数据库查询用户信息
		SysUser sysUser = null;
		try {
			sysUser = sysService.findSysUserByUserCode(userCode);
		} catch (Exception e1) {
			e1.printStackTrace();
		}
		// 如果查询不到返回null
		if(sysUser==null){
			return null;
		}
		
		// 从数据库查询到密码
		String password = sysUser.getPassword();
		//盐
		String salt = sysUser.getSalt();

		// 如果查询到,返回认证信息AuthenticationInfo
		//activeUser就是用户身份信息
		ActiveUser activeUser = new ActiveUser();
		activeUser.setUserid(sysUser.getId());
		activeUser.setUsercode(sysUser.getUsercode());
		activeUser.setUsername(sysUser.getUsername());
		//..
		
		//根据用户id取出菜单
		List<SysPermission> menus  = null;
		try {
			//通过service取出菜单 
			menus = sysService.findMenuListByUserId(sysUser.getId());
		} catch (Exception e) {
			e.printStackTrace();
		}
		//将用户菜单,设置到activeUser
		activeUser.setMenus(menus);

		//将activeUser设置simpleAuthenticationInfo
		SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
				activeUser, password,ByteSource.Util.bytes(salt), this.getName());
		return simpleAuthenticationInfo;
	}

}

5、登陆:

(1)原理:使用FormAuthenticationFilter的过滤器实现。

①在用户没有认证时,请求loginUrl进行认证,用户身份和用户密码提交到loginUrl;

②FormAuthenticationFilter拦截住,并取出request中的username和password(两个参数名称可以配置)

③FormAuthenticationFilter调用realm传入一个token(即username和password);

④realm认证是根据username查询用户信息,(并在ActiveUser中存储,包括userid、usercode、username、menus等),如果查询不到,realm返回null,FormAuthenticationFilter向requset域中填充一个参数(记录了异常信息)。

(2)登陆页面:

由于FormAuthenticationFilter的用户身份和密码的input的默认值(username和password),修改页面的账号和密码 的input的名称为username和password。

(3)登陆代码实现:

@Controller
public class LoginController {

	//loginUrl指定的认证提交地址
	@RequestMapping("/login.action")
	public String login(HttpServletRequest request) throws Exception{
		
		//如果登陆失败,则从request中获取认证异常信息,shiroLoginFailure就是shiro异常类的全限定名
		String exceptionClassName=request.getParameter("shiroLoginFailure");
		//根据shiro返回的异常路径判断,抛出指定异常信息
		if(exceptionClassName!=null){
			if(UnknownAccountException.class.getName().equals(exceptionClassName)){
				throw new CustomException("账户不存在");
			}else if(IncorrectCredentialsException.class.getName().equals(exceptionClassName)){
				throw new CustomException("用户名/密码错误");
			}else{
				throw new Exception();
			}
		}
		
		//此方法不处理登陆成功(认证成功),如果shiro认证成功会自动跳转到上一个请求路径。
		
		//登陆失败回到login页面:
		return "login";
	}
}

(4)认证拦截器:/** = authu 

SSM整合Shiro进行登陆认证和授权详细配置

6、退出登陆:

在shiro中,不需要我们去实现退出登陆接口,只要去访问一个退出的url(该url是可以不存在),由LogoutFilter拦截住,清除session。

SSM整合Shiro进行登陆认证和授权详细配置

7、认证信息在页面显示:

在controller层取出用户信息,并设置在Attribute中,first.action会跳转到首页页面。

	@RequestMapping("/first.action")
	public String first(Model model)throws Exception{
		
		//从shiro的session中取activeUser
		Subject subject = SecurityUtils.getSubject();
		//取身份信息
		ActiveUser activeUser = (ActiveUser) subject.getPrincipal();
		//通过model传到页面
		model.addAttribute("activeUser", activeUser);
		
		return "/first";
	}

、至此,Shiro+Spring+SpringMVC+Mybatis整合实现登陆认证和退出登陆的功能就完成了。启动工程进行测试,在你没有登陆认证成功之前,访问项目中的任何路径,都会被强制跳转到loginUrl指定的路径进行登陆提交。只有登陆认证成功,才可以访问项目中的内容。

 

8、配置Shiro授权过滤器:使用PermissionsAuthorizationFilter

在applicationContext-shiro.xml中配置url所对应的权限。

测试流程:

(1)在applicationContext-shiro.xml中配置filter规则

/permissionTest = perms[item:query]   //即访问“/permissionTest“” 路径需要“item:query”权限

(2)用户在认证通过后,请求“/permissionTest”,被PermissionsAuthorizationFilter拦截,发现需要“item:query”权限;

(3)PermissionsAuthorizationFilter 调用 doGetAuthorizationInfo 获取数据库中的正确权限并返回;

(4)PermissionsAuthorizationFilter对“item:query”和从realm中获取的权限进行对比,如果“item:query”在realm返回的权限列表中,授权通过。

9、创建授权失败页面refuse.jsp,并配置自动跳转:(记录一个在此处遇到的小问题)

如果授权失败,跳转到refuse.jsp,需要在spring容器中配置。

(1)授权失败,页面没有自动跳转:

SSM整合Shiro进行登陆认证和授权详细配置

 在一开始,使用上面这种方法进行配置,但是配置完之后,发现授权失败之后,页面并没有自动跳转,而是直接抛出异常。

2)原因:通过查看Shiro的源码:

private void applyUnauthorizedUrlIfNecessary(Filter filter) {
        String unauthorizedUrl = getUnauthorizedUrl();
        if (StringUtils.hasText(unauthorizedUrl) && (filter instanceof AuthorizationFilter)) {
            AuthorizationFilter authzFilter = (AuthorizationFilter) filter;
            //only apply the unauthorizedUrl if they haven't explicitly configured one already:
            String existingUnauthorizedUrl = authzFilter.getUnauthorizedUrl();
            if (existingUnauthorizedUrl == null) {
                authzFilter.setUnauthorizedUrl(unauthorizedUrl);
            }
        }
    }

 发现是因为shiro源代码中判断了filter是否为AuthorizationFilter,只有perms,roles,ssl,rest,port才是属于AuthorizationFilter,而anon,authcBasic,auchc,user是AuthenticationFilter,所以unauthorizedUrl设置后不起作用。

(3)解决方法:异常全路径做key,错误页面做value。

	<!-- 解决shiro配置的没有权限访问时,unauthorizedUrl不跳转到指定路径的问题 -->
	<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <property name="exceptionMappings">
            <props>
                <prop key="org.apache.shiro.authz.UnauthorizedException">/refuse</prop>
            </props>
        </property>
    </bean>

10、shiro常见的默认过滤器:

过滤器简称

对应的java类

anon

org.apache.shiro.web.filter.authc.AnonymousFilter

authc

org.apache.shiro.web.filter.authc.FormAuthenticationFilter

authcBasic

org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter

perms

org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter

port

org.apache.shiro.web.filter.authz.PortFilter

rest

org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter

roles

org.apache.shiro.web.filter.authz.RolesAuthorizationFilter

ssl

org.apache.shiro.web.filter.authz.SslFilter

user

org.apache.shiro.web.filter.authc.UserFilter

logout

org.apache.shiro.web.filter.authc.LogoutFilter

anon:例子/admins/**=anon 没有参数,表示可以匿名使用。

authc:例如/admins/user/**=authc表示需要认证(登录)才能使用,FormAuthenticationFilter是表单认证,没有参数

perms:例子/admins/user/**=perms[user:add:*],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/**=perms[“user:add:*,user:modify:*”],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。

user:例如/admins/user/**=user没有参数表示必须存在用户, 身份认证通过或通过记住我认证通过的可以访问,当登入操作时不做检查。

11、授权:重写自定义realm的doGetAuthorizationInfo方法,从数据库查询权限信息。

通常使用注解式授权方法和Jsp标签授权方法。

//自定义的Realm
public class CustomRealm extends AuthorizingRealm{

	//注入service
	@Autowired
	private SysService sysService;
	// 设置realm的名称
	@Override
	public void setName(String name) {
		super.setName("customRealm");
	}
	
	//授权的方法
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		
		//从principals获取主身份信息
		//将getPrimaryPrincipal方法返回值转为真实身份类型(在上边doGetAuthenticationInfo认证通过填充到SimpleAuthenticationInfo中的身份类型)
		ActiveUser activeUser=(ActiveUser)principals.getPrimaryPrincipal();
		
		//根据身份信息获取权限信息:从数据库获取到权限数据
		List<SysPermission> permissionList = null;
		try{
			permissionList = sysService.findPermissionListByUserId(activeUser.getUserid());
		}catch (Exception e) {
			e.printStackTrace();
		}
		
		//单独定一个集合对象
		List<String> permissions = new ArrayList<String>();
		if(permissionList!=null){
			for(SysPermission sysPermission:permissionList){
				//将数据库中权限标签符放入集合
				permissions.add(sysPermission.getPercode());
			}
		}
	
		//查到权限数据,返回授权信息(要包括上边的permissions)
		SimpleAuthorizationInfo simpleAuthorizationInfo =new SimpleAuthorizationInfo();
		simpleAuthorizationInfo.addStringPermissions(permissions);//这里添加用户有的权限列表
		simpleAuthorizationInfo.addRole("manager");//这里添加用户所拥有的角色
		
		return simpleAuthorizationInfo;
	}
}

12、授权:开启controller类的aop支持:

对系统中类的方法给用户授权,建议在controller层进行方法授权。

在springmvc.xml文件中配置:

	<!-- 使用注解驱动:自动配置处理器映射器与处理器适配器 -->
	<mvc:annotation-driven />
	<!-- 开启aop,对类代理 -->
	<aop:config proxy-target-class="true"></aop:config>
	<!-- 开启shiro注解支持 -->
	<bean
		class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
		<property name="securityManager" ref="securityManager" />
	</bean>

13、授权:在controller方法中添加注解:

	//权限测试方法
	@RequestMapping("/permissionTest")
	@RequiresPermissions("item:update")//执行此方法需要"item:update"权限
	public String permissionTest(Model model){
		
		return "/permissionTest";
	}

至此,项目启动后,当访问“/permissionTest”时,如果用户没有“item:update”权限,将会自动跳转到refuse.jsp页面;如果如果用户拥有该权限,就会跳转到permissionTest.jsp页面。

14、授权:Jsp标签授权:(permissionTest.jsp)

(1)jsp页面添加:

<%@ tagliburi="http://shiro.apache.org/tags" prefix="shiro" %>

(2)jsp标签授权:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ tagliburi="http://shiro.apache.org/tags" prefix="shiro" %>
<html>
    <body>
        <shiro:authenticated>
        	这是登陆之后 才可以看到的内容
        </shiro:authenticated><br/>
        <shiro:notAuthenticated>
        	这是未登陆的时候 才可以看到的内容
        </shiro:notAuthenticated><br/>
        <shiro:hasRole name="manager">
        	这是拥有商品管理员角色才可以看到的内容
        </shiro:hasRole><br/>
        <shiro:lacksRole name="manager">
      		  这是没有商品管理员角色才可以看到的内容
        </shiro:lacksRole><br/>
        <shiro:hasPermission name="item:update">
        	这是拥有item:update资源权限才可以看到的内容
        </shiro:hasPermission><br/>
        <shiro:lacksPermission name="item:update">
    	    这是没有item:update资源权限才可以看到的内容
        </shiro:lacksPermission><br/>
        <shiro:lacksPermission name="user:query">
    	    这是没有user:query资源权限才可以看到的内容
        </shiro:lacksPermission><br/>
    </body>
</html>

至此,当用户成功进入到授权成功页面时,只能看到符合自己所属权限和角色的内容。

(3)常见的shiro授权标签:

标签名称

标签条件(均是显示标签内容)

<shiro:authenticated>

登录之后

<shiro:notAuthenticated>

不在登录状态时

<shiro:guest>

用户在没有RememberMe时

<shiro:user>

用户在RememberMe时

<shiro:hasAnyRoles name=”abc,123″ >

在有abc或者123角色时

<shiro:hasRole name=”abc”>

拥有角色abc

<shiro:lacksRole name=”abc”>

没有角色abc

<shiro:hasPermission name=”abc”>

拥有权限资源abc

<shiro:lacksPermission name=”abc”>

没有abc权限资源

<shiro:principal>

显示用户身份名称

<shiro:principal property=”username”/> 

显示用户身份中的属性值

15、授权测试:

(1)当调用controller的一个方法,由于该 方法加了@RequiresPermissions(“item:query”) ,shiro调用realm获取数据库中的权限信息,看”item:query”是否在权限数据中存在,如果不存在就拒绝访问,如果存在就授权通过。

(2)当展示一个jsp页面时,页面中如果遇到<shiro:hasPermission name=”item:update”>,shiro调用realm获取数据库中的权限信息,看item:update是否在权限数据中存在,如果不存在就拒绝访问,如果存在就授权通过。

16、完成:

至此,使用Shiro整合Spring+SpringMVC+Mybatis进行登陆认证和授权就完成了,但是在这里,授权的时候存在一个问题,只要遇到注解或jsp标签的授权,都会调用realm方法查询数据库,因此需要使用缓存解决此问题。

 

最后,推荐几篇有关Shiro的文章:

https://www.cnblogs.com/learnhow/p/5694876.html

https://www.sojson.com/shiro#so358852059

https://www.xttblog.com/?p=1272

 

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/114709.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • 素数环

    素数环素数环时间限制:1000 ms|内存限制:65535 KB难度:2素数环时间限制:1000 ms|内存限制:65535 KB难度:2

  • ”在禁用UAC时,无法激活此应用“问题

    ”在禁用UAC时,无法激活此应用“问题”在禁用UAC时,无法激活此应用“问题

  • 激光slam综述_激光点云处理

    激光slam综述_激光点云处理1:SLAM是什么SLAM是同步定位与地图构建(SimultaneousLocalizationAndMapping)的缩写,最早由HughDurrant-Whyte和JohnJ.Leonard提出。SLAM主要用于解决移动机器人在未知环境中运行时定位导航与地图构建的问题。SLAM通常包括如下几个部分,特征提取,数据关联,状态估计,状态更新以及特征更新等。其中包括2D-SLAM和3D-SLAM。一下大概分为三种形式:Localization:在给定地图的情况下,估计机器人的位姿。SLA

  • 使用tinyxml2库解析xml

    使用tinyxml2库解析xmltinyxml2简介tinyxml2是c++编写的轻量级的xml解析器,而且是开放源代码的,在一些开源的游戏引擎中用的比较多。源码托管在github上。源码地址:https://github.com/leethomason/tinyxml2tinyxml2使用起来非常简单,下载源码后无需编译成lib文件,直接將tinyxml2.h和tinyxml2.cpp两个文件添加到你自己的工程中即可。

  • 视频列表

    视频列表

    2021年10月20日
  • html简单登录页面代码[通俗易懂]

    html简单登录页面代码[通俗易懂]图片必须是在Imges下的否则显示不出来(复制代码的话把图片换成你的图片就好了)代码如下&lt;html&gt;&lt;head&gt;&lt;title&gt;tes

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号