博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Springboot+shiro基于url身份认证和授权认证
阅读量:5876 次
发布时间:2019-06-19

本文共 9602 字,大约阅读时间需要 32 分钟。

shiro看了有一段时间了。但是由于之前对这部分理解不了所以在这上面学习的进展一直不多。但是有了解权限管理在日常开发中很重要,所以硬着头皮也要啃下来。 实现功能:

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

shiro的四大组件:

  • 身份认证(Authentication)-证明用户身份,通常叫做登陆(login)。
  • 授权(Authorization)-访问控制
  • 加密(Cryptography)-保护或隐藏数据
  • 会话管理(session management)每个用户时间敏感状态

Shiro内置过滤器,可以实现权限相关的拦截器

  • 常用的过滤器:

  • anon: 无需认证(登录)可以访问

  • authc: 必须认证才可以访问

  • user: 如果使用rememberMe的功能可以直接访问

  • perm: 该资源必须得到资源权限才可以访问

  • 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;@Mapperpublic 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 */@Configurationpublic class ShiroConfig {    /**     * 创建ShiroFilterFactoryBean     */    @Bean    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();        //设置安全管理器        shiroFilterFactoryBean.setSecurityManager(securityManager);        Map
filtersMap = new LinkedHashMap<>(); filtersMap.put("rolesFilter",new rolesFilter()); shiroFilterFactoryBean.setFilters(filtersMap);//使用自定义fitter //添加Shiro内置过滤器 /** * Shiro内置过滤器,可以实现权限相关的拦截器 * 常用的过滤器: * anon: 无需认证(登录)可以访问 * authc: 必须认证才可以访问 * user: 如果使用rememberMe的功能可以直接访问 * perm: 该资源必须得到资源权限才可以访问 * role: 该资源必须得到角色权限才可以访问 */ Map
filterMap = new LinkedHashMap
(); 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版本没问题。

转载于:https://juejin.im/post/5cf7def4f265da1b6f436419

你可能感兴趣的文章
前端工程化系列[01]-Bower包管理工具的使用
查看>>
使用 maven 自动将源码打包并发布
查看>>
Spark:求出分组内的TopN
查看>>
Python爬取豆瓣《复仇者联盟3》评论并生成乖萌的格鲁特
查看>>
关于跨DB增量(增、改)同步两张表的数据小技巧
查看>>
飞秋无法显示局域网好友
查看>>
学员会诊之03:你那惨不忍睹的三层架构
查看>>
vue-04-组件
查看>>
Golang协程与通道整理
查看>>
解决win7远程桌面连接时发生身份验证错误的方法
查看>>
C/C++ 多线程机制
查看>>
js - object.assign 以及浅、深拷贝
查看>>
python mysql Connect Pool mysql连接池 (201
查看>>
Boost在vs2010下的配置
查看>>
一起谈.NET技术,ASP.NET伪静态的实现及伪静态的意义
查看>>
20款绝佳的HTML5应用程序示例
查看>>
string::c_str()、string::c_data()及string与char *的正确转换
查看>>
11G数据的hive初测试
查看>>
如何使用Core Text计算一段文本绘制在屏幕上之后的高度
查看>>
==和equals区别
查看>>