Shiro——安全框架、简述、示例(登录、新增、修改密码)

Ⅰ、简述

1)概述
轻量级灵活的安全框架,用于身份认证、授权、企业会话管理和加密
2)执行原理
更正:realm是抽象类(画图时想当然了,忘记还有抽象类这个东西)
在这里插入图片描述

3)shiro认证、授权
在这里插入图片描述

Ⅱ、登录示例

这里使用 spring管理的web项目 来举例

一、准备

1)引入jar包

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

2)web.xml中配置shiro过滤器

<!--shiro过滤器-->
    <filter>
        <filter-name>shiroFilter</filter-name>
        <!--spring代理过滤器,拦截所有请求,通过代理过滤器找到shiro过滤器-->
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <init-param>
            <param-name>targetFilterLifecycle</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

3)spring的xml中初始化过滤器、securityMAnager、realm

<?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 
https://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--初始化shiro过滤器-->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <!--登录-->
        <property name="loginUrl" value="/index.html"></property>
        <!--登录成功-->
        <property name="successUrl" value="/main.jsp"></property>
        <!--Unauthorized未经授权(当前用户不能访问某些页面时,跳转到指定页面)-->
        <property name="unauthorizedUrl" value="/index.html"></property>
        <property name="filterChainDefinitions">
            <value>
                <!--authc认证、perms授权、roles角色-->
                <!--BOSS这个角色可以访问的页面-->
                /jurisdiction.html = authc, roles[BOSS]
                
                <!--编号为022的功能可读-->
                <!--/jurisdiction.html = authc, perms[022:read]-->

                <!--退出:清除session、改变cookie中的登录状态-->
                /employee/loginOut = logout
                
                <!--释放:这些资源、链接、路径可以被公众访问(就是没登录,也可以访问的……)-->
                /index.html = anon
                /employee/login = anon
                /css/** = anon
                /js/** = anon
                
                 <!--所有路径都需要认证,除了释放的-->
                /** = authc
            </value>
        </property>
    </bean>
    <!--初始化securityMAnager-->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="myRealm"></property>
    </bean>
    
    <!--管理shiro生命周期的对象。可以对类重构(重新构建)-->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
    
    <!--初始化realm-->
    <bean id="myRealm" class="com.hbw.realm.MyRealm">
        <property name="credentialsMatcher">
            <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
            	<!--指定加密算法-->
                <property name="hashAlgorithmName" value="MD5"></property>
                <!--迭代次数(随机数:盐,采用MD5算法迭代了几次)-->
                <property name="hashIterations" value="3"></property>
            </bean>
        </property>
    </bean>
    
</beans>

二、使用

1)前端

index.html登录页面发送请求

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
    <link rel="stylesheet" type="text/css" href="/css/themes/default/easyui.css">
    <link rel="stylesheet" type="text/css" href="/css/themes/icon.css">
    <link rel="stylesheet" type="text/css" href="/css/demo/demo.css">
    <script type="text/javascript" src="/js/jquery.min.js"></script>
    <script type="text/javascript" src="/js/jquery.easyui.min.js"></script>
</head>
<body>
<div style="margin:20px 0;"></div>
<div style="margin: auto;width: 400px;">
<div class="easyui-panel" title="登录页面" style="width:400px">
    <div style="padding:10px 60px 20px 60px">
        <form id="ff" method="post">
            <table cellpadding="5">
                <tr>
                    <td>用户名:</td>
                    <td><input class="easyui-textbox" type="text" name="ename" data-options="required:true"></input></td>
                </tr>
                <tr>
                    <td>密码:</td>
                    <td><input class="easyui-textbox" type="password" name="epwd" data-options="required:true"></input></td>
                </tr>

            </table>
        </form>
        <div style="text-align:center;padding:5px">
            <a href="javascript:void(0)" class="easyui-linkbutton" onclick="submitForm()">登录</a>
            <a href="javascript:void(0)" class="easyui-linkbutton" onclick="clearForm()">取消</a>
        </div>
    </div>
</div>
</div>

<script>
    function submitForm(){
        $('#ff').form('submit', {
            url:"/login",
            success:function(data){
                var obj = eval('('+data+')');
                alert(obj.message);
                if(obj.result){
                    if(obj.login){
                        window.location.href="/main.html";
                    }else{
                        window.location.href ="/index.html";
                    }
                }else{
                    window.location.href = "/index.html";
                }
            }
        });
    }
    function clearForm(){
        $('#ff').form('clear');
    }
</script>
</body>
</html>

2)controller控制层

前端发送请求给控制层,控制层调用servie层

/*登录*/
    @RequestMapping("login")
    @ResponseBody
    public EmployeeResult login(String ename, String epwd){
        //使用shiro登录
        return employeeService.shiroLogin(ename,epwd);
    }

3)service层进行处理

service进行逻辑处理,login方法自动调用MyRealm.java

public EmployeeResult shiroLogin(String ename, String epwd) {
        EmployeeResult result = new EmployeeResult();
        //通过核心管理器,拿到登录对象
        Subject subject = SecurityUtils.getSubject();

        try{//校验成功
            //登录校验(把用户名和密码封装到token中)
            // 调用login实际上是交给realm做的操作
            subject.login(new UsernamePasswordToken(ename,epwd));
            //利用shiro也可以讲数据封装在session中
            Session session = subject.getSession();
            //shiro讲登录成功的数据封装在subject的principal中
            session.setAttribute("emplyoee",subject.getPrincipal());
            result.setLogin(true);
            result.setResult(true);
            result.setMessage("登录成功");
        }catch (AuthenticationException ex){//校验失败
            ex.printStackTrace();
            result.setLogin(false);
            result.setMessage("登录失败");
        } catch (Exception e) {
            e.printStackTrace();
            result.setResult(false);
        }

        return result;
    }

4)MyRealm.java

登录调用认证方法

定义realm进行认证、授权,realm调用mapper.xml与数据库打交道,根据获取的数据进行校验,如果校验没有通过,则会抛出异常,在Srvice中进行逻辑判断即可。

package com.hbw.realm;

import com.hbw.bean.Efunction;
import com.hbw.bean.Employee;
import com.hbw.bean.EmployeeExample;
import com.hbw.bean.Erole;
import com.hbw.dao.EfunctionMapper;
import com.hbw.dao.EmployeeMapper;

import com.hbw.dao.EroleMapper;
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.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;

public class MyRealm extends AuthorizingRealm {
    @Autowired
    private EmployeeMapper employeeMapper;
    @Autowired
    private EroleMapper eroleMapper;
    @Autowired
    private EfunctionMapper efunctionMapper;
    //授权(就是授予权力,你有没有权力某些链接、路径的权力,防止非法访问)
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        AuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        Employee employee = (Employee) principalCollection.getPrimaryPrincipal();
        //1)获取当前用户的角色信息
            //每个员工可能有多个角色
            List<Erole> eroles = eroleMapper.getRoleByEid(employee.getEid());
            //将角色信息封装到AuthorizationInfo中
            for(Erole erole : eroles){
                ((SimpleAuthorizationInfo) authorizationInfo).addRole(erole.getRname());
            }
        //2)获取当前登录客户的功能信息
            List<Efunction> efunctions = efunctionMapper.getCurrentEfunction(employee.getEid());
            //将功能信息封装到AuthorizationInfo中
            for(Efunction efunction : efunctions){
                ((SimpleAuthorizationInfo) authorizationInfo).addStringPermission(efunction.getFcode());
            }

        return authorizationInfo;
    }

    //认证(就是登录校验)
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        //如何认证的
        //先通过用户名查询用户信息,没有就是null,抛出异常
        EmployeeExample example = new EmployeeExample();
        EmployeeExample.Criteria criteria = example.createCriteria();
        criteria.andEnameEqualTo(token.getUsername());
        List<Employee> employees = employeeMapper.selectByExample(example);
        if(employees!=null && employees.size()>0){
            //使用返回的用户信息中的用户名和密码与token中的用户名和密码进行校验(查询的信息、与前端的信息来比较)。认证失败就抛出异常
            //三个参数:查询出的对象、查询对象中的密码、token中的用户名
            SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(employees.get(0),employees.get(0).getEpwd(),token.getUsername());
            //指定盐(getRemark1获取新增用户时保存在remark1字段中的盐)
            authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes(employees.get(0).getRemark1()));
            return authenticationInfo;
        }
        return null;
    }
}

5)dao层
查询数据而已,省略

Ⅲ、新增用户,密码加密示例

/*新增员工,并加密*/
    @Override
    @Transactional
    public BaseResult addCurrentEmployee(Employee employee) {
        logger.info("开始新增,参数:"+employee);
        BaseResult result = new BaseResult();
        try{
            //添加之前校验工号唯一
            EmployeeExample example = new EmployeeExample();
            EmployeeExample.Criteria criteria = example.createCriteria();
            criteria.andJobNumberEqualTo(employee.getJobNumber());
            List<Employee> employeeList = employeeMapper.selectByExample(example);
            if(employeeList!=null && employeeList.size()>0){
                result.setResult(false);
                result.setMessage("工号重复");
                return result;
            }else{
                //获取随机数(别称:盐)
                //二进制转为十六进制
                String salt = new SecureRandomNumberGenerator().nextBytes().toHex();
                //使用shiro加密(原密码、随机数、迭代三次)
                //使用MD5算法进行加密
                String newepwd = new Md5Hash(employee.getEpwd(),salt,3).toString();
                employee.setEpwd(newepwd);
                //把盐也保存下来(登录、修改时使用)
                employee.setRemark1(salt);
                
                employeeMapper.insert(employee);
                
                result.setResult(true);
                result.setMessage("添加成功");
            }
        }catch (Exception ex){
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            ex.printStackTrace();
            result.setResult(false);
            result.setMessage("添加失败");
        }
        return result;
    }

Ⅳ、修改加密的密码示例

/**
     *
     * @param epwd 输入的旧密码
     * @param newepwd  输入的新密码
     * @return
     */
    public BaseResult changePassword(String epwd,String newepwd) {
        BaseResult result = new BaseResult();
        Session session = SecurityUtils.getSubject().getSession();
        //获取当前用户的id
        Employee employee1 = (Employee)session.getAttribute("emplyoee");
        int eid = employee1.getEid();
        //查询输入的旧密码是否正确(加密比较)
        Employee employee = employeeMapper.selectByPrimaryKey(eid);
        String saltold = employee.getRemark1();
        String epwdold = new Md5Hash(epwd,saltold,3).toString();
        if(epwdold.equals(employee.getEpwd())){
            //加密修改
            String salt = new SecureRandomNumberGenerator().nextBytes().toHex();
            String newepwd1 = new Md5Hash(newepwd,salt,3).toString();
            employee1.setEpwd(newepwd1);
            employee1.setRemark1(salt);
            employeeMapper.updateByPrimaryKeySelective(employee1);
            result.setResult(true);
            result.setMessage("密码修改成功");
        }else{
            result.setMessage("旧密码不正确!!!");
            result.setResult(false);
            return result;
        }
        return result;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈年_H

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值