Shiro_登陆

四大基石:密码学  会话管理   登陆   授权

目录

1.Shiro集成Spring导包

2.Web.xml的配置

3.applicationContext-shiro.xml的配置

4.创建JpaRealm

5.Spring配置文件中引入shiro的配置文件

 

6.密码设置

1.准备一个加密算法

2.通过算法加密数据库密码

3.添加员工时密码加密

6.准备登陆页面

login.jsp

2.LoginController跳转

3.静态资源放行

8.完成登陆功能

   1.完善Service层

2.JsonResult返回数据的封装

9.扩展.

1.回车登陆

2.Main.jsp在资源中可以找到标签配置

3.登陆过期

4.Shiro的配置问题


1.Shiro集成Spring导包

<!-- shiro的支持包 -->
<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-all</artifactId>
  <version>1.4.0</version>
  <type>pom</type>
</dependency>
<!-- shiro与Spring的集成包 -->
<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-spring</artifactId>
  <version>1.4.0</version>
</dependency>

 

2.Web.xml的配置

      <!-- Spring与shiro集成:需要定义一个shiro过滤器(这是一个代理过滤器,它会到spring的配置中找一个名称相同的真实过滤器) -->
<filter>
  <filter-name>shiroFilter</filter-name>
  <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.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-3.0.xsd">


    <!-- 1.配置apache的管理器 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <!-- 配置一个realm,到数据库中获取权限数据 -->
        <property name="realm" ref="jpaRealm"/>
    </bean>


    <!-- 2.我们可以自定义一个realm【暂时未实现功能】这个必需实现org.apache.shiro.realm.Realm接口 -->
    <bean id="jpaRealm" class="cn.itsource.wy.shiro.realm.JpaRealm">
      <!--加密-->
        <property name="credentialsMatcher">
            <!--设置加密匹配方案-->
            <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                <!-- 编码的方式使用:md5 -->
                <property name="hashAlgorithmName" value="MD5"/>
                <!-- 编码的次数:10 -->
                <property name="hashIterations" value="10"/>
            </bean>
        </property>
    </bean>

    <!-- 3.lifecycleBeanPostProcessor:可以自动调用在Spring Ioc窗口中 Shiro bean的生成周期方法 -->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

    <!-- 4.启动ioc容器中使用 shiro的注解,但是必需配置在Spring Ioc容器中Shiro bean的生成周期方法 -->
    <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>

    <!-- 5.shiro的真实过滤器(注:这个名称必需和web.xml的代表过滤器【DelegatingFilterProxy】名称一样) -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <!-- 登录的url,如果没有登录,你访问的路径会跳到这个页面 -->
        <property name="loginUrl" value="/login"/>
        <!-- 登录成功的url,如果登录成功,会跳转到这个页面 -->
        <property name="successUrl" value="/main"/>
        <!-- 没有权限时跳转到这个位置 -->
        <property name="unauthorizedUrl" value="/s/unauthorized.jsp"/>
        <!--
            配置哪些资源被保护,哪些资源需要权限
            anon:不需要登录也可以访问相应的权限
            authc:需要权限才能访问
              /** :所有文件及其子文件
        -->
       <!-- <property name="filterChainDefinitions">
            <value>
                /s/login.jsp = anon
                /** = authc
            </value>
        </property>-->
        <property name="filterChainDefinitionMap" ref="filterChainDefinitionMap"></property>
    </bean>

    <!-- 这个bean是帮助咱们获取相应的值:它会到一个工厂bean中通过对应的方法拿到相应的值 -->
    <bean id="filterChainDefinitionMap" factory-bean="filterChainDefinitionMapBuilder" factory-method="createFilterChainDefinitionMap"></bean>
    <!-- 配置可以创建 -->
    <bean id="filterChainDefinitionMapBuilder" class="cn.itsource.wy.shiro.realm.FilterChainDefinitionMapBuilder"></bean>
</beans>

 

4.创建JpaRealm

    public class JpaRealm extends AuthenticatingRealm{
    @Autowired
    private IEmployeeService employeeService;

    //AuthenticationInfo:认证; 身份验证; 证明
    //登录的时候就会调用这个方法来做验证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //身份认证(用户名)
        // 1.拿到用户名
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken)token;
        String username = usernamePasswordToken.getUsername();

        // 2.根据用户名到数据库拿到用户
        Employee loginUser = employeeService.findByUsername(username);
        if(loginUser==null){
            return null;
            //该用户名不存在
        }
        //从数据库中拿到密码
        Object credentials = loginUser.getPassword();

        //加盐的数据
        ByteSource byteSource = ByteSource.Util.bytes("itsource");
        return new SimpleAuthenticationInfo(loginUser,credentials,byteSource,getName());
    }
}

 

5.Spring配置文件中引入shiro的配置文件

<!-- 引入shiro的配置文件-->
<import resource="applicationContext-shiro.xml"/>

 

 

 

6.密码设置

1.准备一个加密算法

public class MD5Util {
    // String algorithmName, Object source, Object salt, int hashIterations
    //设置盐值
    public static final String SALT = "itsource";
    //设置遍历次数
    public static final int HASHITERATIONS = 10;
    //传入一个字符串,返回一个md5编码的值
    public static String createMd5Str(String str){
        SimpleHash hash = new SimpleHash("MD5",str,SALT,HASHITERATIONS);
        return hash.toString();
    }
}

2.通过算法加密数据库密码

   public class MD5Test extends BaseTest {

    @Autowired
    private IEmployeeService employeeService;

    //密码加密
    @Test
    public void testUpdatePwd() throws Exception{
        List<Employee> list = employeeService.findAll();
        for (Employee employee : list) {

          /*  //密码初始化(与用户名一致)
            employee.setPassword((employee.getUsername()));*/

            employee.setPassword(MD5Util.createMd5Str(employee.getPassword()));
            employeeService.save(employee); //注:save是添加与修改
        }
    }
}

3.添加员工时密码加密

   EmployeeServiceImpl

@Override
public void save(Employee employee) {
    //新增保存修改
    if(employee.getId() == null){
        employee.setPassword(MD5Util.createMd5Str(employee.getPassword()));
    }

    super.save(employee);
}

 

6.准备登陆页面

  1. login.jsp

   <%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>源码智销系统</title>
    <%@ include file="/WEB-INF/views/head.jsp"%>
    <script type="text/javascript">
        //登录页面处理过期问题
        if(top != window){
           top.location.href = "/login";
        }
        //登录代码
        function submitForm(){
            //提交表单
            $("#loginForm").form('submit',{
                url:"/login",
                onSubmit:function(){
                    //提交之前的验证 验证成功在提交
                    return $(this).form('validate');
                },
                success:function(data){
                    //{success:true;msg:'信息'}
                    var result = $.parseJSON(data);
                    if(result.success){
                        //跳转到成功页面 js跳转页面
                        location.href = '/main';
                    }else{
                        //打印错误信息 提示给用户看
                        $.messager.show({
                            title:'温馨提示',
                            msg:'登录问题:'+result.msg,
                            timeout:5000,
                            showType:'slide'
                        });

                    }
                }

            })
        }
        //回车登录
        $(function(){
            //绑定事件
            $(document.documentElement).on("keyup",function(event){
                //event对象
                var keyCode = event.keyCode;
                if(keyCode === 13){
                    submitForm();
                }
            });
        });

/*背景样式*/
    </script>
<%--    <style type="text/css">
    body {
        background: linear-gradient(to left, powderblue, pink);
    }
</style>--%>
</head>
<body>

<video src="media/ww.MP3" autoplay loop controls hidden></video>

<div align="center" style="margin-top: 100px;">
    <div class="easyui-panel" title="智销系统用户登陆" style="width: 400px; height: 350px;">
        <form id="loginForm" class="easyui-form" method="post">
            <table align="center" style="margin-top: 15px;">
                <tr height="20">
                    <td>用户名:</td>
                </tr>
                <tr height="10">
                    <td><input name="username" class="easyui-validatebox" required="true" value="admin" /></td>
                </tr>
                <tr height="20">
                    <td>密 码:</td>
                </tr>
                <tr height="10">
                    <td><input name="password" type="password" class="easyui-validatebox" required="true" value="0" /></td>
                </tr>
                <tr height="40">
                    <td align="center"><a href="javascript:" class="easyui-linkbutton" οnclick="submitForm();">登陆</a> <a
                            href="javascript:;" class="easyui-linkbutton" οnclick="resetForm();">重置</a></td>
                </tr>
            </table>
            <%--验证码--%>
            <div align="center">
                验证码:<input type="text" name="code" style="margin-top: 30px" > <br>
                <img src="/kaptcha" οnclick="img(this)" width="150x"  style="margin-top: 10px"><br>
                <input type="button" value="看不清? 换一张" id="btn" style="margin-top: 5px">
            </div>
        </form>
    </div>
</div>
</body>
<%--验证码点击切换下一张--%>
<script type="text/javascript">

    document.getElementById("btn").onclick = function () {

        document.getElementsByTagName("img")[0].src="/kaptcha?id="+Math.random();


    }
</script>

</html>

 

2.LoginController跳转

     @Controller
public class LoginController {
    //用于完成跳转
    @RequestMapping(value="/login",method = RequestMethod.GET)
    public String index(){
        return "login";
    }


    //用于完成登录
    @RequestMapping(value="/login",method = RequestMethod.POST)
    @ResponseBody
    public JsonResult login(String username, String password, String code, HttpServletRequest req, HttpServletResponse resp){

        /*验证码验证*/
        /**
         * 获取当前的 Subject. 调用 SecurityUtils.getSubject();
         * 测试当前的用户是否已经被认证. 即是否已经登录. 调用 Subject 的 isAuthenticated()
         * 若没有被认证, 则把用户名和密码封装为 UsernamePasswordToken 对象
         * 执行登录: 调用 Subject 的 login(AuthenticationToken) 方法.
         */

        String yzm = req.getParameter("code");

        // 验证码未输入
        if (yzm == null || "".equals(yzm)) {
            // 抛出自定义异常(继承AuthenticationException), Shiro会捕获AuthenticationException异常
            // 发现该异常时认为登录失败,执行登录失败逻辑,登录失败页中可以判断如果是CaptchaEmptyException时为验证码为空
            return new JsonResult(false,"请输入验证码");
        }
        // 获取SESSION中的验证码
        // Kaptcha在生成验证码时会将验证码放入SESSION中
        // 默认KEY为KAPTCHA_SESSION_KEY, 可以在Web.xml中配置
        String sessionCaptcha = (String) SecurityUtils.getSubject().getSession().getAttribute("KAPTCHA_SESSION_KEY");
        // 比较登录输入的验证码和SESSION保存的验证码是否一致
        if (!yzm.equals(sessionCaptcha)) {
            // 抛出自定义异常(继承AuthenticationException), Shiro会捕获AuthenticationException异常
            // 发现该异常时认为登录失败,执行登录失败逻辑,登录失败页中可以判断如果是CaptchaEmptyException时为验证码错误
            return new JsonResult(false,"验证码错误");
        }

        //1.拿到访问的主体(当前登录用户)
        Subject subject = SecurityUtils.getSubject();
        //2.判断这个用户是否已经登录(通过验证)
        if(!subject.isAuthenticated()){
            //3.如果没有验证,就要完成登录
            UsernamePasswordToken token = new UsernamePasswordToken(username,password);
            try{
                //4.根据toke完成登录功能
                subject.login(token);
            }catch (UnknownAccountException e){
                System.out.println("用户名不存在!!");
                e.printStackTrace();
                return new JsonResult(false,"账号或者密码出错!");
            }catch (IncorrectCredentialsException e){
                System.out.println("密码不存在!");
                e.printStackTrace();
                return new JsonResult(false,"账号或者密码出错!");
            }catch (AuthenticationException e){
                System.out.println("登录出错!");
                e.printStackTrace();
                return new JsonResult(false,"程序发生未知错误!");
            }

        }
        return new JsonResult();
    }
    //登出方法
    @RequestMapping("/logout")
    public String logout(){
        //登出
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return "redirect:/login";
    }
}

 

3.静态资源放行

     public class FilterChainDefinitionMapBuilder {
    public Map<String,String> createFilterChainDefinitionMap(){
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap();
        //注:对于一些不登录也可以放行的设置(大家可以根据实际情况添加)
        filterChainDefinitionMap.put("/login","anon");
        filterChainDefinitionMap.put("*.js","anon");
        filterChainDefinitionMap.put("*.css","anon");
        filterChainDefinitionMap.put("/css/**","anon");
        filterChainDefinitionMap.put("/js/**","anon");
        filterChainDefinitionMap.put("/easyui/**","anon");
        filterChainDefinitionMap.put("/images/**","anon");
        filterChainDefinitionMap.put("/media/**","anon");
        filterChainDefinitionMap.put("/kaptcha","anon");
        //这个值之后从数据库中查询到【用户-角色-权限-资源】
        //filterChainDefinitionMap.put("/s/permission.jsp","perms[user:*]");
        //filterChainDefinitionMap.put("/s/employee.jsp","perms[employee:*]");
        filterChainDefinitionMap.put("/**","authc");
        return  filterChainDefinitionMap;
    }
}

 

8.完成登陆功能

   1.完善Service层

    //根据用户名查询用户
public Employee findByUsername(String username);

 

@Override
public Employee findByUsername(String username) {
    return employeeRepository.findByUsername(username);
}

 

2.JsonResult返回数据的封装

 //返回数据的封装
public class JsonResult {

    private boolean success = true;

    private String  msg ;


    public boolean isSuccess() {
        return success;
    }

    public void setSuccess(boolean success) {
        this.success = success;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public JsonResult(){}

    public JsonResult(boolean success,String msg){
        this.success = success;
        this.msg = msg;
    }
}

 

9.扩展.

1.回车登陆

    //回车登录
$(function(){
    //绑定事件
    $(document.documentElement).on("keyup",function(event){
        //event对象
        var keyCode = event.keyCode;
        if(keyCode === 13){
            submitForm();
        }
    });
});

 

2.Main.jsp在资源中可以找到标签配置

方案一:

    <div style="float:right" >
    欢迎
    <span style="color:#ff5766">
            <shiro:user>
                <shiro:principal property="name"/>
            </shiro:user>
        </span>
    登录,<a href="/logout">退出</a>
</div>

方案二:

<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>

...

<shiro:user>
    欢迎[ <shiro:principal property="username"  />]登录,
    <a href="${pageContext.request.contextPath}/logout">退出</a>
</shiro:user>

 

3.登陆过期

   //登出方法
@RequestMapping("/logout")
public String logout(){
    //登出
    Subject subject = SecurityUtils.getSubject();
    subject.logout();
    return "redirect:/login";
}

 

//登录页面处理过期问题
if(top != window){
    top.location.href = "/login";
}

 

4.Shiro的配置问题

  <!--设置加密匹配方案-->
<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值