Springboot+shiro基于url身份认证和受权认证

shiro看了有一段时间了。可是因为以前对这部分理解不了因此在这上面学习的进展一直很少。可是有了解权限管理在平常开发中很重要,因此硬着头皮也要啃下来。 实现功能:html

  • 身份认证
  • 对不一样页面进行url受权
  • 多表登陆解决
  • 同一个页面多role访问

项目完整github地址 欢迎starjava

springboot一些学习整合完整地址git

shiro的四大组件:github

  • 身份认证(Authentication)-证实用户身份,一般叫作登录(login)。
  • 受权(Authorization)-访问控制
  • 加密(Cryptography)-保护或隐藏数据
  • 会话管理(session management)每一个用户时间敏感状态

Shiro内置过滤器,能够实现权限相关的拦截器web

  • 经常使用的过滤器:spring

  • anon: 无需认证(登陆)能够访问sql

  • authc: 必须认证才能够访问数据库

  • user: 若是使用rememberMe的功能能够直接访问apache

  • perm: 该资源必须获得资源权限才能够访问api

  • role: 该资源必须获得角色权限才能够访问

这里面只用到了身份认证和受权,权限认证只用到了一点点,shiro的原理是封装的过滤器,他可以在访问浏览器前能过自动完成一些内容。

shiro配置主要两部分——shiroconfig和自定义的Realm(继承AuthorizingRealm)。其中,shiroconfig是shiro的主要配置文件,而自定义的Realm主要是重写AuthorizingRealm的两个方法,分别是身份认证和受权认证调用数据库查询比对。而若是须要role访问则须要重写一个filter。

前奏

项目结构:

在这里插入图片描述

环境:

  • Springboot2
  • mybatis
  • shiro

新建表:

在这里插入图片描述

对应的bean:

package com.shiro.bean;

public class student {
    private String username;
    private String password;
    private String role;
    private String perm;
   //省略get set

复制代码

mybatis简单查询:

package com.shiro.mapper;


import com.shiro.bean.student;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

@Mapper
public interface studentMapper {

    @Select("select * from student where username=#{name}")
    student findByName(String name);
}

复制代码

省略html和sql,详细能够到GitHub下载

页面目录,:

shiro核心配置文件(rolesFilter可选)。

UserRealm.java

package com.shiro.config;

import com.shiro.bean.student;
import com.shiro.mapper.studentMapper;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;


/** * 自定义Realm * @author bigsai * */
public class UserRealm extends AuthorizingRealm{

	@Autowired(required = false)
	private studentMapper studentMapper;
	private final Logger logger= LoggerFactory.getLogger(UserRealm.class);
	/** * 执行受权逻辑 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
		logger.info("执行逻辑受权");

		//给资源进行受权
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

		//添加资源的受权字符串
		//到数据库查询当前登陆用户的受权字符串
		//获取当前登陆用户
		Subject subject = SecurityUtils.getSubject();
		student user = (student) subject.getPrincipal();
		student dbUser = studentMapper.findByName(user.getUsername());

		info.addRole(user.getRole());//添加role 和perms role表明角色 perms表明操做,或者动做等。用于颗粒化权限管理
		info.addStringPermission(dbUser.getPerm());
		System.out.println("user:"+dbUser.getPerm());
		return info;
	}
	/** * 执行认证逻辑 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {
		System.out.println("执行认证逻辑");
		//编写shiro判断逻辑,判断用户名和密码
		//1.判断用户名
		UsernamePasswordToken token = (UsernamePasswordToken)arg0;

		student user = studentMapper.findByName(token.getUsername());

		if(user==null){
			//用户名不存在
			return null;//shiro底层会抛出UnKnowAccountException
		}

		//2.判断密码
		return new SimpleAuthenticationInfo(user,user.getPassword(),"");
	}

}

复制代码

rolesFilter

package com.shiro.config;

import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

// AuthorizationFilter抽象类事项了javax.servlet.Filter接口,它是个过滤器。
public class rolesFilter extends AuthorizationFilter {

    @Override
    protected boolean isAccessAllowed(ServletRequest req, ServletResponse resp, Object mappedValue) throws Exception {
        Subject subject = getSubject(req, resp);
        String[] rolesArray = (String[]) mappedValue;

        if (rolesArray == null || rolesArray.length == 0) { //没有角色限制,有权限访问
            return true;
        }
        for (int i = 0; i < rolesArray.length; i++) {
            if (subject.hasRole(rolesArray[i])) { //若当前用户是rolesArray中的任何一个,则有权限访问
                return true;
            }
        }
        return false;
    }
}
复制代码

shiroConfig:shiro的主要配置

package com.shiro.config;

import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;


/** * Shiro的配置类 * * @author bigsai */
@Configuration
public class ShiroConfig {

    /** * 建立ShiroFilterFactoryBean */
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        //设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(securityManager);


        Map<String, Filter> filtersMap = new LinkedHashMap<>();
        filtersMap.put("rolesFilter",new rolesFilter());
        shiroFilterFactoryBean.setFilters(filtersMap);//使用自定义fitter
        //添加Shiro内置过滤器
        /** * Shiro内置过滤器,能够实现权限相关的拦截器 * 经常使用的过滤器: * anon: 无需认证(登陆)能够访问 * authc: 必须认证才能够访问 * user: 若是使用rememberMe的功能能够直接访问 * perm: 该资源必须获得资源权限才能够访问 * role: 该资源必须获得角色权限才能够访问 */
        Map<String, String> filterMap = new LinkedHashMap<String, String>();



        filterMap.put("/login", "anon");//要将登录的接口放出来,否则没权限访问登录的接口
        filterMap.put("/getcontroller", "anon");
//
        //受权过滤器
        //注意:当前受权拦截后,shiro会自动跳转到未受权页面
        filterMap.put("/add", "perms[add]");
        filterMap.put("/update", "perms[update]");

//
        filterMap.put("/test1.html","rolesFilter[admin,user]");
        filterMap.put("/*", "authc");//authc即为认证登录后便可访问

        
        //修改调整的登陆页面
        shiroFilterFactoryBean.setLoginUrl("/index");
        //设置未受权提示页面
        shiroFilterFactoryBean.setUnauthorizedUrl("/noAuth");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);


        return shiroFilterFactoryBean;
    }

    /** * 建立DefaultWebSecurityManager */
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //关联realm
        securityManager.setRealm(userRealm);
        return securityManager;
    }

    /** * 建立Realm */
    @Bean(name = "userRealm")
    public UserRealm getRealm() {
        return new UserRealm();
    }

}

复制代码

身份认证

身份认证,就是登陆校检。这是第一层过滤,而且当用户没有登陆的时候,回退到没登录的界面。在controller中,login的核心为:

@RequestMapping("/login")
    public String login(String name, String password, Model model, HttpServletRequest request) {

        model.addAttribute("nama", "给个star");
        /** * 使用Shiro编写认证操做 */
        //1.获取Subject
        Subject subject = SecurityUtils.getSubject();

        //2.封装用户数据
        UsernamePasswordToken token = new UsernamePasswordToken(name, password);

        //3.执行登陆方法
        try {
            subject.login(token);

            //登陆成功
            //跳转
            return "redirect:/index2";
        } catch (UnknownAccountException e) {
            //e.printStackTrace();
            //登陆失败:用户名不存在
            model.addAttribute("msg", "用户名不存在");
            return "login";
        } catch (IncorrectCredentialsException e) {
            //e.printStackTrace();
            //登陆失败:密码错误
            model.addAttribute("msg", "密码错误");
            return "login";
        }
    }
复制代码

releam中

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {
		System.out.println("执行认证逻辑");
		//编写shiro判断逻辑,判断用户名和密码
		//1.判断用户名
		UsernamePasswordToken token = (UsernamePasswordToken)arg0;

		student user = studentMapper.findByName(token.getUsername());
		if(user==null){
			//用户名不存在
			return null;//shiro底层会抛出UnKnowAccountException
		}
		//2.判断密码
		return new SimpleAuthenticationInfo(user,user.getPassword(),"");
	}
复制代码

而这只是表象的处理过程,而在releam(继承AuthorizingRealm)中须要充血doGetAuthenticationInfo()方法.

大体流程为:登陆——>拿帐号密码检验———>用着token的帐号经过你的sql查询对象——>比对数据是否一致——>经过仍是抛各类异常

而在shiroConfig中,基于url过滤时authc便可访问

多表登陆源如何操做?

可能会遇到以下状况:教师端,学生端来自两张表,两个登陆接口,我该如何使用shiro身份认证。对于这种问题,你能够配置多个releam,可是我以为若是简单你能够在不一样的登陆接口下传递一个参数过来,这个参数就用session传递。由于,shiro的session和网页httprequest得到的session是同一个session

因此当你在login传递一个属性到releam中,可用 if else判断而后不一样登陆接口执行不一样的查询方法便可。

受权管理

接上流程 是否登陆——>是/否——(是)—>查询role/perm添加到subject——>过滤器校验该url须要权限——>能够访问/权限不足

shiro主要url能够根据角色(role)和资源(perm)的管理。对于role,能够是管理员,教师等,而perm,多是一个动做,一个操做,等等。==而且可能一个角色拥有多个role和perm==。 同理,受权就是查询数据库的role或者perm字段添加到角色中。固然具体api不作介绍。 主要方法为上述:

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
		logger.info("执行逻辑受权");

		//给资源进行受权
		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

		//添加资源的受权字符串
		//到数据库查询当前登陆用户的受权字符串
		//获取当前登陆用户
		Subject subject = SecurityUtils.getSubject();
		student user = (student) subject.getPrincipal();
		student dbUser = studentMapper.findByName(user.getUsername());

		info.addRole(user.getRole());//添加role 和perms role表明角色 perms表明操做,或者动做等。用于颗粒化权限管理
		info.addStringPermission(dbUser.getPerm());
		System.out.println("user:"+dbUser.getPerm());
		return info;
	}
复制代码

而url中也是

filterMap.put("/add", "perms[add]");
 filterMap.put("/update", "roles[admin]");
复制代码

如何解决界面多角色/资源问题

经常遇到这种状况:一个接口/页面,有两个或者以上角色能够访问。而后再后台的过滤器配置总。shiro默认的配置是and而不是or。这就须要咱们本身定义filter继承AuthorizationFilter从写对应方法。

以多角色访问为例子。从写上述就是文件rolesFilter。在使用的时候也要首先声明filter才能使用。

在这里插入图片描述

访问效果

在页面受权的

运行测试:访问其余接口都被返回到这个界面

在这里插入图片描述
在这里插入图片描述

登录成功后,界面能够访问

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
有个小注意点:若是mybatis2.0版本回和spring-start-web有冲突。我用1.3.2版本没问题。

项目github地址

springboot一些学习整合完整地址

相关文章
相关标签/搜索