shiro_shiro to springboot 注册

一、概述

使用SpringBoot集成shiro实现登录注册功能,将这部分交给shiro控制,通过shiro的认证来实现

二、shiro_springboot_mybatis_登录注册

功能划分:

  • 登录
  • 注册

代码分为:

  • mybatis - mapper.xml
  • entity - User
  • realm - UserRealm 数据域
  • config - ShiroConfig Shiro配置类
  • dao - UserDao
  • service - UserService (interface) UserServiceImpl (Class)
  • controller - UserController
  • utils - ApplicationContextUtils (获取Bean) SaltUtils (生成随机盐)
  • webapp - jsp 前端展示的登录 注册 主页页面

项目包结构图 - 图示:

image-20220222163741812.png

第一步

先把环境搭建好

导入依赖:

        <!--        shiro依赖-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring-boot-starter</artifactId>
            <version>1.5.3</version>
        </dependency>

        <!--        解析jsp依赖-->
        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-jasper</artifactId>
            <version>9.0.41</version>
        </dependency>
        <!--        jstl标签依赖-->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>

<!--        mybatis依赖-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.3</version>
        </dependency>

<!--        mysql依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.23</version>
        </dependency>
<!--        druid连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.8</version>
        </dependency>

<!--        lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
        </dependency>

SpringBoot配置文件

spring.application.name=shiro
server.servlet.context-path=/shiro
server.port=8899

#视图解析
spring.mvc.view.prefix=/
spring.mvc.view.suffix=.jsp

#datesource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.url=jdbc:mysql://localhost:3306/shiro?Encodeing=UTF-8


#mybatis
mybatis.mapper-locations=classpath:mapper/*.xml

第二步

建表 and 写持久层代码

| t_user | CREATE TABLE `t_user` (
  `id` int NOT NULL AUTO_INCREMENT,
  `username` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `password` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `salt` varchar(16) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb3 |

写Mybatis文件

<?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.geekrose.shiro.dao.UserDao">

    <sql id="userBody">
        id,username,password,salt
    </sql>

    <insert id="save" parameterType="com.geekrose.shiro.entity.User" useGeneratedKeys="true" keyProperty="id">
        insert into t_user
        <trim prefix="values ( " suffix=")" suffixOverrides=",">
            #{id},
            <if test="username != null">
                #{username},
            </if>
            <if test="password != null">
                #{password},
            </if>
            <if test="salt != null">
                #{salt},
            </if>
        </trim>
    </insert>

    <select id="findUserByName" resultType="com.geekrose.shiro.entity.User" parameterType="java.lang.String">
        select <include refid="userBody"></include>
        from t_user where username = #{username}
    </select>
</mapper>

第三步

写业务层代码

接口:

public interface UserService {

    void register(User user);

    User findUserByName(String name);
}

实现类:

@Service("userService")
@Transactional
public class UserServiceImpl implements UserService{

    @Autowired
    private UserDao userDao;

    public void register(User user){

        // 添加账号密码随机盐
        String salt = SaltUtils.getSalt(8);
        user.setSalt(salt);

        // 生成随机密码
        Md5Hash result = new Md5Hash(user.getPassword(), salt, 1024);
        user.setPassword(result.toHex());
        System.out.println(user);
        userDao.save(user);

    }

    public User findUserByName(String name) {
        User user = userDao.findUserByName(name);
        return user;
    }


}

第四步

写工具类代码

获取Bean

@Component
public class ApplicationContextUtils implements ApplicationContextAware {

    private static ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.context = applicationContext;
    }

    public static Object getBean(String beanName){
        Object bean = context.getBean(beanName);
        return bean;
    }

}

生成随机盐

public class SaltUtils {
    public static String getSalt(int length){
        char[] chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890,.;[]/'!@#$%^&*()-=".toCharArray();
        StringBuffer buffer = new StringBuffer();
        for (int i = 0; i < length; i++) {
            buffer.append(chars[new Random().nextInt(chars.length)]);
        }

        return buffer.toString();
    }

    public static void main(String[] args) {
        System.out.println(getSalt(4));
    }
}

第五步

编写控制层代码

@Controller
@RequestMapping("user")
public class UserController {

    @Resource
    private UserService userService;

    @RequestMapping("register")
    public String doRegister(User user){
        System.out.println(user);
        try {
            userService.register(user);
        }catch (Exception e){
            e.printStackTrace();
            return "redirect:/register.jsp";
        }
        return "redirect:/login.jsp";

    }


    @RequestMapping("login")
    public String doLogin(String username,String password){
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);

        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();

        try {
            subject.login(token);
            return "redirect:/index.jsp";
        }catch (UnknownAccountException e){
            e.printStackTrace();
            System.out.println("账号错误");
        }catch (IncorrectCredentialsException e){
            e.printStackTrace();
            System.out.println("密码错误");
        }

        return "redirect:/login.jsp";

    }

    @RequestMapping("logout")
    public String doLogout(){
        Subject subject = SecurityUtils.getSubject();
        subject.logout();
        return "redirect:/login.jsp";
    }

}

第六步

编写shiro相关代码

ShiroConfig配置类

@Configuration
public class ShiroConfig {

    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){

        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
        HashMap<String, String> map = new HashMap<>();
        map.put("/login.jsp","anon");
        map.put("/register.jsp","anon");
        map.put("/user/login","anon");
        map.put("/user/register","anon");

        map.put("/**","authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);

        return shiroFilterFactoryBean;
    }

    @Bean
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("getRealm") Realm getRealm){

        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(getRealm);

        return defaultWebSecurityManager;
    }

    @Bean
    public Realm getRealm(){

        UserRealm userRealm = new UserRealm();
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
        matcher.setHashIterations(1024);
        matcher.setHashAlgorithmName("md5");
        userRealm.setCredentialsMatcher(matcher);
        return userRealm;

    }

}

UserRealm 数据域

public class UserRealm extends AuthorizingRealm {
//    授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }


//    认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

        String principal = (String) authenticationToken.getPrincipal();

        UserService userService = (UserService) ApplicationContextUtils.getBean("userService");
        User user = userService.findUserByName(principal);

        if (!ObjectUtils.isEmpty(user)){
            return new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(),ByteSource.Util.bytes(user.getSalt()),this.getName());
        }

        return null;
    }
}

第七步

编写前端显示的jsp页面

index.jsp 主页

<%@page contentType="text/html; utf-8" language="java" isELIgnored="false" pageEncoding="UTF-8" %>
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <h2>Hello Shiro index page to SpringBoot</h2>
    <h3><a href="${pageContext.request.contextPath}/user/logout">登出shiro</a></h3>
    <ul>
        <li><a href="">用户模块</a></li>
        <li><a href="">产品模块</a></li>
        <li><a href="">组件模块</a></li>
        <li><a href="">竞价模块</a></li>
    </ul>
</body>
</html>

login.jsp 登录

<%@page contentType="text/html; utf-8" language="java" isELIgnored="false" pageEncoding="UTF-8" %>
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <h2>Hello Shiro login page to SpringBoot</h2>
    <form action="${pageContext.request.contextPath}/user/login" method="post">
        username: <input type="text" name="username"> <br>
        password: <input type="text" name="password"> <br>
        <input type="submit" value="登录">
    </form>
    <h3><a href="register.jsp">前往注册</a></h3>
</body>
</html>

register.jsp 注册

<%@page contentType="text/html; utf-8" language="java" isELIgnored="false" pageEncoding="UTF-8" %>
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <h2>Hello Shiro register page to SpringBoot</h2>
    <form action="${pageContext.request.contextPath}/user/register" method="post">
        username: <input type="text" name="username"> <br>
        password: <input type="text" name="password"> <br>
        <input type="submit" value="立即注册">
    </form>
</body>
</html>

最后显示效果:

登录:

image-20220222164958271.png

注册:

image-20220222165009869.png

主页:


image-20220222165028594.png

三、分析

shiro部分

  • 这里在ShiroConfig配置类中关于数据域的注入中使用了md5加密、加盐、散列操作

  • 控制层的认证部分交给Shiro管理

    登录

    subject.login(token);
    

    登出

    subject.logout();
    

    注册:通过向用户表插入 用户记录方式

    插入密码时 使用 md5加密 + 加盐 + 散列方式 后存储

    并且将随机盐存储在内

  • 登录的校验

    因为相同明文 进行md5 加密生成的密文一定是唯一的,所以可以进行 md5加密后和数据库中的密文对比,相同就登入成功(重定向到主页)

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容