Spring+Shiro整合之身份认证

1.shiro

Apache Shiro是一个强大且易用的Java安全框架。
shiro四大基石–身份验证,授权,会话管理,加密。
Authentication(身份认证):即我们平时所说的“登录”。

2.整合

2.1 整合说明
在spring项目中,都是通过spring来管理bean,如果想要使用shiro,就需要将shiro整合到spring。集成Spring的核心就是把框架的核心类(Subject,SecurityManager,Realm)交给Spring管理。
2.2整合步骤

  • 在pom.xml中导入shiro-all
<!-- 日志slf4j-log4j12   -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>1.7.12</version>
    </dependency>
    <!-- shiro-all -->
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-all</artifactId>
      <version>1.3.2</version>
    </dependency>
  • 在/WEB-INF/web.xml中配置shiro的过滤器
<!--配置shiro的过滤器-->
    <filter>
        <filter-name>shiroFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <init-param>
            <param-name>transformWsdlLocations</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
  • 在resources下创建spring-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">
    <!--创建域,主要是处理用户认证-->
    <bean id="authRealm" class="com.sunyue.ssm.web.shiro.realm.AuthRealm">
        <property name="credentialsMatcher">
            <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                <!--配置加密方式,必须与注册时候加密方式一致-->
                <property name="hashAlgorithmName" value="MD5"/>
                <!--配置加密次数,必须与注册时候加密次数一致-->
                <property name="hashIterations" value="3306"/>
            </bean>
        </property>
    </bean>
    <!--创建安全认证管理器-->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="authRealm"></property>
    </bean>
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"></property>
        <!--对登录的资源路径放行,因为使用了过滤器,所以原则上来说,所有资源全部访问不了-->
        <property name="loginUrl" value="/login.jsp"></property>
        <!--对没有权限时默认的界面放行,比如错误界面-->
        <property name="unauthorizedUrl" value="error.jsp"></property>
        <!--对认证成功也就是登录成功之后的资源放行,一般配置后台的主界面路径-->
        <!--<property name="successUrl" value=""></property>-->
        <property name="filterChainDefinitions">
            <value>
                <!--对静态资源不拦截-->
                /static/*=anon <!--以后,css,js,bootstrap,vue,layui等静态资源全部放在static目录下-->
                /user/login=anon <!--对登录的后台action进行放行-->
                <!--配置/user/list.do只有有user权限的用户才能访问
                /user/list.do=perms[user:list]
                -->
                <!--声明/user/logout.do使用logout的过滤器
                该过滤器会自动帮我们清除退出登录时要清除的东西
                -->
                /user/logout=logout
                <!--以下资源进行拦截-->
                /*=authc
                /*/*=authc
            </value>
        </property>
    </bean>

</beans>

代码

  • 结构
    在这里插入图片描述
  • User.java
package com.sunyue.ssm.bean;

import java.util.Date;

public class User {
    private int id;
    private String username;
    private String password;
    private String gender;
    private String phone;
    private Date birthday;
    private String pic;
    private int delFlag;

    public User() {
    }

    public User(String username) {
        this.username = username;
    }

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getPic() {
        return pic;
    }

    public void setPic(String pic) {
        this.pic = pic;
    }

    public int getDelFlag() {
        return delFlag;
    }

    public void setDelFlag(int delFlag) {
        this.delFlag = delFlag;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", gender='" + gender + '\'' +
                ", phone='" + phone + '\'' +
                ", birthday=" + birthday +
                ", pic='" + pic + '\'' +
                ", delFlag=" + delFlag +
                '}';
    }
}

  • UserDao.java
package com.sunyue.ssm.dao;

import com.sunyue.ssm.bean.User;

public interface UserDao {
    /**
     * 登录
     * @param user
     * @return
     */
    public User login(User user);

    /**
     * 注册(加密)
     * @param user
     * @return
     */
    public int saveUser(User user);
}

  • UserDao.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.sunyue.ssm.dao.UserDao">
    <resultMap id="findUserResultMap" type="User">
        <id column="id" property="id"/>
        <result column="username" property="username"/>
        <result column="password" property="password"/>
        <result column="gender" property="gender"/>
        <result column="phone" property="phone"/>
        <result column="birthday" property="birthday"/>
        <result property="pic" column="pic"/>
    </resultMap>
    <!--登录-->
    <select id="login" parameterType="User" resultMap="findUserResultMap">
        select * from users where username=#{username}
    </select>
    <insert id="saveUser" parameterType="User">
        insert into users(username,password) values (#{username},#{password})
    </insert>
</mapper>
  • IUserService.java
package com.sunyue.ssm.service;

import com.sunyue.ssm.bean.User;

public interface IUserService {
    /**
     * 用户登录
     * @param user
     * @return
     * @throws Exception
     */
    public User login(User user)throws Exception;

    /**
     * 注册(加密)
     * @param user
     * @return
     */
    public int saveUser(User user)throws Exception;
}

  • UserService.java
package com.sunyue.ssm.service.impl;

import com.sunyue.ssm.bean.User;
import com.sunyue.ssm.dao.UserDao;
import com.sunyue.ssm.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional(value = "transactionManager")
public class UserService implements IUserService {
    @Autowired
    private UserDao userDao;
    /**
     * 用户登录
     *
     * @param user
     * @return
     * @throws Exception
     */
    @Transactional(propagation = Propagation.NOT_SUPPORTED,readOnly = true)
    @Override
    public User login(User user) throws Exception {
        return userDao.login(user);
    }

    /**
     * 注册(加密)
     *
     * @param user
     * @return
     */
    @Override
    public int saveUser(User user) throws Exception {
        return userDao.saveUser(user);
    }
}

  • UserTest.java
  • 在这里插入图片描述
package com.sunyue.ssm.test;


import com.sunyue.ssm.bean.User;
import com.sunyue.ssm.service.IUserService;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.util.ByteSource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-bean.xml")
public class UserTest {
    @Autowired
    private IUserService userService;
    @Test
    public void test1() throws Exception {
        //shiro的加密:MD5加密方式+盐值+加密次数
        //需要进行MD5加密
        String algorithmName ="MD5";//加密方式
        Object source ="111";//这是加密源,也就是需要加密的密码
        //为了提高加密的安全性,或者说复杂度,进行加盐值,随便,要求加密和认证的时候的盐值一样
        ByteSource salt = ByteSource.Util.bytes("十里长街千堆雪");
        //为了进一步提高加密的复杂度,进行加密次数的设置
        int hashIterations = 3306;
        //进行加密
        SimpleHash sh = new SimpleHash(algorithmName,source,salt,hashIterations);
        System.out.println("加密前加密后密码对比----加密前:111,加密后:"+sh.toString());
        User users = new User("pomelo",sh.toString());//加密后的密码
        int i = userService.saveUser(users);
        System.out.println(i);
    }
}

测试结果:
在这里插入图片描述
查看数据库是否成功创建用户且加密密码:
在这里插入图片描述

  • UserController.java
package com.sunyue.ssm.controller;

import com.sunyue.ssm.bean.User;
import com.sunyue.ssm.service.IUserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.http.HttpSession;

@Controller
@RequestMapping("/user")
public class UserController {
    private static final transient Logger log = LoggerFactory.getLogger(UserController.class);

    @Autowired
    private IUserService userService;

    /**
     * 登录
     * @param user
     * @return
     */
    @RequestMapping(value = "/login",method = RequestMethod.POST)
    public String login(User user,HttpSession session){
        log.info("===================>UserController.java-->login() start");
        //创建Subject
        Subject currentUser = SecurityUtils.getSubject();
        //将用户名和密码封装到token中
        UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(),user.getPassword());
        if (!currentUser.isAuthenticated()){//未登录
            log.info("==========>UserController.java-->login()-->currentUser.isAuthenticated()");
            try {
                //使用subject提供的login方法进行认证
                log.info("============>UserController.java-->login()-->currentUser.login(token) start");
                currentUser.login(token);
                log.info("============>UserController.java-->login()-->currentUser.login(token)  end");
                log.info("登录成功"+token.getPrincipal());
                return "redirect:/good/showGood";
            } catch (UnknownAccountException uae) {
                log.info("没有用户名为的用户" + token.getPrincipal());
                return "redirect:/login.jsp";
            }
            // 若账户存在, 但密码不匹配, 则 shiro 会抛出 IncorrectCredentialsException 异常。
            catch (IncorrectCredentialsException ice) {
                log.info("密码帐户 " + token.getPrincipal() + " 是不正确的!");
                return "redirect:/login.jsp";
            }
            // 用户被锁定的异常 LockedAccountException
            catch (LockedAccountException lae) {
                log.info("用户名账户 " + token.getPrincipal() + "被锁定.  " + "请联系管理员解锁.");
                return "redirect:/login.jsp";
            }catch (AuthenticationException e){
                log.info("认证失败 ");
                return "redirect:/login.jsp";
            }
        }
        log.info("------userController.java-->login()   end");
        log.info("登录失败!");
        return "redirect:/login.jsp";
    }
}

  • AuthRealm.java
package com.sunyue.ssm.web.shiro.realm;

import com.sunyue.ssm.bean.User;
import com.sunyue.ssm.service.IUserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.realm.AuthenticatingRealm;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * 主要是进行登录的验证
 */
public class AuthRealm extends AuthenticatingRealm {
    @Autowired
    private IUserService userService;
    /**
     * 进行登录验证
     * @param token (这个token就是UserController中封装的UsernamePasswordToken)
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("=====>AuthRealm.java-->doGetAuthenticationInfo()");
        UsernamePasswordToken upt = (UsernamePasswordToken) token;
        //获取登录的用户名
        String username = upt.getUsername();//前台的用户名
        try {
            //通过数据库进行验证
            User u = userService.login(new User(username));//封装要认证的数据
            Object principal = u.getUsername();//需要认证的身份:主体:用户名进行认证(用户名来自数据库)
            Object credentials = u.getPassword();//认证的凭证:就是我们的密码(密码来自数据库)
            ByteSource credentialsSalt = ByteSource.Util.bytes("十里长街千堆雪");
            return new SimpleAuthenticationInfo(principal,credentials,credentialsSalt,getName());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

至此,登录加密已经实现。
现在登录,就只能用pomelo这个账号进行登录,而admin这个账号因为密码未进行加密处理,而登录从前台传过来的密码是进行加密过的,与数据库中的不匹配,所以admin就不能登录成功。
前台页面见:https://blog.csdn.net/SUNxiaodui/article/details/108584955(SSM:Spring+SpringMVC+MyBatis整合之登录+商品信息分页展示(IDEA))

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值