saas-export:shiro认证和授权

Shiro过滤器&标签简介

判断 sesion中是否有 user 判断账户密码是否正确

(1)分析
需要过滤器控制 没有权限下访问链接
需要标签控制 没有权限下 界面元素的隐藏
》anon代表不认证也可以访问,通常对静态资源进行放行
》authc代表必须通过认证才可以访问,通常对动态资源(controller,jsp页面)进行拦截,如果用户没有认证,Shiro会自动跳转到login.jsp页面

Shiro登陆认证-判断session中的user

(1)过滤器
在项目中使用认证过滤器拦截资源(该拦截的拦截,该放行的放行)
(2)authc认证过滤器
必须认证才能访问,如果未认证跳登录页
(3)anon过滤器
放行,不需要拦截认证
(4)拦截路径问题

/* 
/**

**applicationContext-shiro.xml**

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   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.xsd">

		<!--shiro搭建2  2.3 配置shiroFilter 认证或授权逻辑处理对象-->
		<!--注意:这里的shiroFilter必须和web.xml的filter-name保持一致-->
		<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
				<!--注入SecurityManager-->
				<property name="securityManager" ref="securityManager"/>
		</bean>

		<!--2.1.创建 SecurityManager-->
		<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
				<!--注入Realm-->
				<property name="realm" ref="authRealm"/>
		</bean>

		<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
				<!--注入SecurityManager-->
				<property name="securityManager" ref="securityManager"/>
				<property name="filterChainDefinitions">
						<value>
								<!-- 如果请求提静态资源 anon 不要求session中有user,直接可以访问-->
								/css/**=anon
								/img/**=anon
								/plugins/**=anon
								<!-- 如果请求login或者regist方法 anon-->
								/system/user/login=anon
								/system/user/regist=anon
								<!-- 如果除以上之后的xxx 要求登录 authc查询session中是否有user-->
								/**=authc
						</value>
				</property>
				<!--如果 session就是没有user,表示未登录,页面转到login.jsp -->
				<property name="loginUrl" value="/login.jsp"/>

		</bean>
		<!--使用自己定义的匹配器-->
		<bean id="credentialsMatcher2"  class="com.xy.utils.CustomCredentialsMatcher">
		</bean>
		<!--2.2.创建Realm-->
		<bean id="authRealm" class="com.xy.shiro.AuthRealm">
				<property name="credentialsMatcher" ref="credentialsMatcher2"/>
		</bean>

</beans>

Shiro登录认证-用户密码判断

Shiro登陆认证三种结果

(1)正确
无异常
User user = (User)subject.getPrincipal()//获取user对象
(2)用户不存在
程序抛出UnknownAccountException异常
(3)用户密码出错
程序抛出IncorrectCredentialsException异常

Shiro登陆认证login方法

(1) 获取Subject对象
Subject subject = SecurityUtils.getSubject();
(2)调用Subject的认证方法 : login 本质:AuthRealm的认证方法
subject.login(token);
(3)登录的结果
1、 正确
2、用户不存在
3、用户密码出错

UserController

userController通地subject连接到securitymanager,再由securityManager去调用realm

@RequestMapping(path = "/login")
		public String login(String email, String password, HttpSession session, HttpServletRequest request){
				l.info("login email="+email);
				l.info("login password="+password);

				Subject subject = SecurityUtils.getSubject();
				UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(email, password);

				try {
						subject.login(usernamePasswordToken);
						l.info("login 登录成功");

						User user = (User) subject.getPrincipal();
						l.info("login user="+user);

						session.setAttribute("user",user);

						List<Module> menus = iModuleService.findModulesByUser(user);
						session.setAttribute("menus",menus);
						l.info("login menus "+menus);
						//跳到主页
						return "redirect:/home/toMain.do";
				} catch (IncorrectCredentialsException e){//密码出错
						e.printStackTrace();
						l.info("密码不对");
						request.setAttribute("error","邮箱或者密码不对");
						return "forward:/login-shiro.jsp";
				}
		}

AuthRealm

//不需要再添加@Component 因为在xml中使用bean标签配置了
//AuthorizingRealm 认证(登录账号密码)和 授权(查有什么权限)
public class AuthRealm extends AuthorizingRealm {
		private Logger l = LoggerFactory.getLogger(this.getClass());
		@Autowired
		private IModuleService iModuleService;
		@Autowired
		private IUserService userService;
		//spring是通过反射调用无参构造函数
		public AuthRealm(){
				l.info("AuthRealm 无参构造函数执行了");
		}
		//授权(查有什么权限)
		@Override
		protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
				l.info("AuthRealm doGetAuthorizationInfo 函数执行了");

				Subject subject = SecurityUtils.getSubject();
				User user = (User) subject.getPrincipal();
				if (user!=null){
						//查询权限
						List<Module> modules = iModuleService.findModulesByUser(user);
						SimpleAuthorizationInfo info =new SimpleAuthorizationInfo();
						for (Module m:modules){
								l.info("AuthRealm AuthorizationInfo m="+m.getName());
								info.addStringPermission(m.getName());
						}
						return info;
				}
				return null;
		}
		//认证(登录账号密码)
		@Override
		protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
				l.info("AuthRealm doGetAuthenticationInfo 函数执行了");
				//响应login方法和getPrincipal方法的调用
				UsernamePasswordToken usernamePasswordToken= (UsernamePasswordToken) token;

				String email  = usernamePasswordToken.getUsername();
				l.info("doGetAuthenticationInfo --"+email);
				//调service读dao
				User user =  userService.findUserByEmail(email);
				l.info("doGetAuthenticationInfo --"+user);
				if (user == null) {
						//用户不存在
						return null;//-->系统会将null转成UnknownAccountException抛出
				} else {
						//用户存在的
						//AuthenticationInfo 返回给 User user = (User) subject.getPrincipal();
						/**
						 * 参数一:principal,存放用户登录信息,subject.getPrincipal()获取
						 * 参数二:数据库的密码
						 * 参数三:realm的别名,只有在多个Realm的时候才会用,一般不用
						 */
						AuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), "");
						return info;
				}
		}
}

MD5Utils


public class MD5Utils {
    //
    public static String stringToMD5(String plainText) {//参1 明文
        byte[] secretBytes = null;
        try {
            secretBytes = MessageDigest.getInstance("md5").digest(
                    plainText.getBytes());//对明文的字节进行摘要
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("没有这个md5算法!");
        }
        //16或者32
        String md5code = new BigInteger(1, secretBytes).toString(16);

        //补0 一个字节转两位的16进制
        for (int i = 0; i < 32 - md5code.length(); i++) {
            md5code = "0" + md5code;
        }
        return md5code;
    }
}

    @Test
    publi
c void stringToMD5() {
        String result = MD5Utils.stringToMD5("123");
        System.out.println(result);
        //md5(123)= 202cb962ac59075b964b07152d234b70
        //202cb962ac59075b964b07152d234b70
    }

    //shiro也集成常用的加密的算法md5,sha-1

    @Test
    public void test02() {
        Md5Hash md5Hash = new Md5Hash("123");//参1 传入明文
        System.out.println(md5Hash.toString());
        //202cb962ac59075b964b07152d234b70
    }

凭证匹配器-加盐加密
(1)为什么要加盐
如果只是单纯使用md5或sha1进行加密,容易被人利用"彩虹表"撞库来破解密码,导致密码不安全!这时可以进行加盐加密来解决。
演示: 加盐
(2)shiro加盐加密
1)编写代码对密码加盐加密

//原来保存用户使用的密码是明文,现在需要对它进行加密
        if(user.getPassword()!=null){
            Md5Hash md5Hash = new Md5Hash(user.getPassword(),user.getEmail());//参1 明文  参2 盐
            user.setPassword(md5Hash.toString());
        }

2)编写自定义凭证匹配器


public class CustomCredentialsMatcher extends SimpleCredentialsMatcher {
		private Logger l = LoggerFactory.getLogger(this.getClass());

		//将密码加密成密文,但需要使用账号作盐
		//subject.login(token) 表示的页面提交的密码 123 -> 密文1
		//info  调relam 表示的是数据中的密码  密文2
		@Override
		public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
				//token获取账号密码
				UsernamePasswordToken t = (UsernamePasswordToken) token;
				String email = t.getUsername();
				//密码是char[] '1','2','3'  => "123"  new String(char[])
				String pwd1 = new String(t.getPassword());
				l.info("doCredentialsMatch pwd1 "+pwd1);
				//加密
				Md5Hash md5Hash = new Md5Hash(pwd1, email);//参1 传入明文  参2盐
				pwd1 = md5Hash.toString();
				l.info("doCredentialsMatch pwd1 "+pwd1);

				//取数据库的密码 此时不用再加密码,因为它就是密文
				String pwd2 = (String) info.getCredentials();
				l.info("doCredentialsMatch pwd2 "+pwd2);
				if (pwd1.equals(pwd2)) {
						return true;//密码正确
				} else {
						return false;//密码不正确
				}

		}
}

3)在applicationContext-shiro.xml,添加自定义凭证匹配器

<!--使用自己定义的匹配器-->
    <bean id="credentialsMatcher2"  class="com.wzx.web.utils.CustomCredentialsMatcher">
    </bean>
    <!--2.2.创建Realm-->
    <bean id="authRealm" class="com.wzx.web.shiro.AuthRealm">
        <property name="credentialsMatcher" ref="credentialsMatcher2"/>
    </bean>

LoginOut

(1)之前我们再退出时候,已经实现了退出后销毁session。
//删除Shiro的session(底层:删除session数据)
Subject subject = SecurityUtils.getSubject();
subject.logout();

    @RequestMapping(path = "/loginOut-shiro", method = {RequestMethod.GET, RequestMethod.POST})
    public String loginOutShiro(){
        //删除session中的用户信息
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return "redirect:/login-shiro.jsp";
    }

shiro授权 介绍

(1)什么是授权?
授权,也叫做授权访问校验
登陆认证后,系统校验用户是否有权限访问资源,就叫授权。
(2)如何实现授权?分为以下两个步骤
》1 登陆认证成功后,获取用户的权限 (获取权限)
》2 访问资源时候,进行授权校验:用访问资源需要的权限去用户权限列表查找,如果存在,则有权限访问资源。(权限拦截)
(3)授权校验有几种方式
1) 硬编码方式(拦截方法)(非Web应用,Web应用)
2) 过滤器配置方式(拦截url)(Web应用)
3) 注解方式(拦截方法)(Web应用)
4) shiro提供的标签((拦截页面元素:按钮,表格等))(Web应用)

第一种访问代码(不推荐) 硬编码

(1)虽然看不到用户管理,但直接输入地址,也可以访问
因此,需要给用户管理添加权限
(2)判断权限方法调用
Subject subject = SecurityUtils.getSubject();
subject.checkPermission(“用户管理”);
会调用:com.wzx.web.shiro.AuthRealm#doGetAuthorizationInfo
(3)如果没有权限则出现报错

第二种访问代码

XML配置方式实现权限管理(推荐)

(1)通过XML配饰方式实现需求: 有用户管理的权限,才可以访问; 否则拒绝访问。
(2)原理:shiro过滤器
拦截请求时,获取授权
(3)在applicationContext-shiro.xml

<!-- 给url配置权限--> /company/toList.do=perms["企业管理"] /system/user/toList.do=perms["用户管理"]

(4)可以指定无权限页面提示

<property name="unauthorizedUrl" value="/unauthorized.jsp"/>

在这里插入图片描述

第三种访问代码

shiro授权 注解方式实现

(1)通过注解配置方式实现需求: 有用户管理的权限,才可以访问; 否则拒绝访问。(先注释掉xml配置部分)
(2)实现步骤
》1 在applicationContext-shiro.xml中开启shiro注解支持

<!-- @RequiredPermission-->
 <bean id="lifecycleBeanPostProcessor"
       class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
 <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
       depends-on="lifecycleBeanPostProcessor"/>
 <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
     <property name="securityManager" ref="securityManager"/>
 </bean>

》2 开启Aop自动代理(已经完成)
springmvc.xml

<aop:aspectj-autoproxy/> 

》3 在controller中使用
@RequiresPermissions("")注解
@RequiresPermissions("企业管理")

第四种访问方式
shiro授权 Shiro标签实现权限管理

(1)新建测试页面
shiro-test.jsp
不同账号进行访问
》1 在页面引入shiro标签 ,类似c标签
<%@ taglib prefix=“shiro” uri=“http://shiro.apache.org/tags” %>
》2 使用shiro标签 给name赋值,赋上什么值就表示需要什么权限

<%--        标签会查询权限,如果没有权限不报错,将页面内容进行隐藏,反之显示页面内容--%>

        <shiro:hasPermission name="企业管理">
            <a href="">企业管理</a>
        </shiro:hasPermission>

        <shiro:hasPermission name="用户管理">
            <a href="">用户管理</a>
        </shiro:hasPermission>

        <shiro:hasPermission name="日志管理">
            <a href="">日志管理</a>
        </shiro:hasPermission>

shiro认证和授权小结

1、认证: subject.login(token);
subject调用login方法进行认证,login方法使用securityManager安全管理器的Authentication认证器认证,Authentication认证器找我们编写AuthorizingRealm的子类进行效验通过则返回AuthenticationInfo对象,否则就抛异常。
在这里插入图片描述

2、授权: subject.checkPermission(“权限名称”)
原理:授权一般在 登陆认证成功后自动查询用户所有权限,并添加到shiro中,然后访问资源时候,进行授权校验:用访问资源需要的权限去用户权限列表查找,如果存在,则有权限访问资源。否则跳转到指定的没有权限提示页面。

流程:通过subject获取登录的用户对象,如果对象不为空,则根据用户查询对应的权限,创建一个SimpleAuthorizationInfo,然后将权限存到SimpleAuthorizationInfo中,并且返回这个对象。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值