Shiro+Spring+SpringMVC+Mybatis+数据库整合并进行登陆认证和授权详细配置

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

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

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

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

		
		
		  
		        org.apache.shiro  
		        shiro-core  
		        1.2.3  
		    
		    
		        org.apache.shiro
		        shiro-ehcache
		        1.2.3
		    
		    
		        org.apache.shiro
		        shiro-web
		        1.2.3
		    
		    
		        org.apache.shiro
		        shiro-spring
		        1.2.3
		    

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


	org.apache.shiro
	shiro-all
	1.2.3

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

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

        
	
	
		shiroFilter
		org.springframework.web.filter.DelegatingFilterProxy
		
		
			targetFilterLifecycle
			true
		
		
		
			targetBeanName
			shiroFilter
		
	
	
		shiroFilter
		/*
	

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



	
	 
		
		
		
		
		
		
		
		
		
 		
			
				
				/images/** = anon
				/js/** = anon
				/styles/** = anon
				/validatecode.jsp =anon
				
				/logout.action = logout
				
				/** = authc
				
				
			
		
	
	
	
	
        
            
                /refuse
            
        
    
	
	
	
		
	
	
	
	
		
		
	

	
	
		
		
		
		
	

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 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 

Shiro+Spring+SpringMVC+Mybatis+数据库整合并进行登陆认证和授权详细配置_第1张图片

6、退出登陆:

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

Shiro+Spring+SpringMVC+Mybatis+数据库整合并进行登陆认证和授权详细配置_第2张图片

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)授权失败,页面没有自动跳转:

Shiro+Spring+SpringMVC+Mybatis+数据库整合并进行登陆认证和授权详细配置_第3张图片

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

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。

	
	
        
            
                /refuse
            
        
    

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 permissionList = null;
		try{
			permissionList = sysService.findPermissionListByUserId(activeUser.getUserid());
		}catch (Exception e) {
			e.printStackTrace();
		}
		
		//单独定一个集合对象
		List permissions = new ArrayList();
		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文件中配置:

	
	
	
	
	
	
		
	

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" %>

    
        
        	这是登陆之后 才可以看到的内容
        
这是未登陆的时候 才可以看到的内容
这是拥有商品管理员角色才可以看到的内容
这是没有商品管理员角色才可以看到的内容
这是拥有item:update资源权限才可以看到的内容
这是没有item:update资源权限才可以看到的内容
这是没有user:query资源权限才可以看到的内容

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

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

标签名称

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

登录之后

不在登录状态时

用户在没有RememberMe时

用户在RememberMe时

在有abc或者123角色时

拥有角色abc

没有角色abc

拥有权限资源abc

没有abc权限资源

显示用户身份名称

 

显示用户身份中的属性值

15、授权测试:

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

(2)当展示一个jsp页面时,页面中如果遇到,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

 

 

你可能感兴趣的:(SSM+SSH框架,Shiro认证授权)