springboot+shiro 实现登录、注册和密码加密(vue+springboot前后端分离)

首先引入依赖

		<dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.30</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.2</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.8.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.5.2</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.21</version>
        </dependency>

        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.45</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

附:数据库配置和log4j在控制台输出日志的配置,在application.properties,加上:

# LOG4J配置
log4j.rootCategory=INFO, stdout
# 控制台输出
log4j.appender.stdout=org.apache.log4j.ConsoleAppender 
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %5p %c{1}:%L - %m%n

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

#别名
mybatis.type-aliases-package=com.sys.domain
# 数据库配置
spring.datasource.driver-class-name =com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/sys?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
spring.datasource.username =root
spring.datasource.password =root
#如果不配置阿里的druid,会自动使用默认的数据源 (com.zaxxer.hikari.HikariDataSource)
spring.datasource.type = com.alibaba.druid.pool.DruidDataSource

自定义类ShiroConfig ,配置shiro

/**
 * shiro配置类
 * @author hp
 */
@Configuration
public class ShiroConfig {

    @Bean(name = "factoryBean")
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        //设置安全管理器
        factoryBean.setSecurityManager(securityManager);

        //添加shiro的内置过滤器
        /*
            anon:无需认证就可以访问
            authc:必须认证了才能访问
            user:必须拥有 记住我 功能才能使用
            perms:拥有对某个资源的权限才能访问
            role:拥有某个角色权限才能访问
         */
        Map<String, String> filterMap = new LinkedHashMap<>();


        factoryBean.setFilterChainDefinitionMap(filterMap);

        return factoryBean;
    }

    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //关联userRealm
        securityManager.setRealm(userRealm);
        return securityManager;
    }

    @Bean
    public UserRealm userRealm(@Qualifier("matcher") HashedCredentialsMatcher matcher){
        UserRealm userRealm = new UserRealm();
        userRealm.setAuthorizationCachingEnabled(false);
        userRealm.setCredentialsMatcher(matcher);
        return userRealm;
    }

    /**
     * 密码匹配凭证管理器
     *
     * @return
     */
    @Bean(name = "matcher")
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        // 采用MD5方式加密
        hashedCredentialsMatcher.setHashAlgorithmName("MD5");
        // 设置加密次数
        hashedCredentialsMatcher.setHashIterations(1024);
        hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
        return hashedCredentialsMatcher;
    }
}

自定义类UserRealm 类,用于配置shiro的授权与认证

public class UserRealm extends AuthorizingRealm {
    @Autowired
    UserService userService;

	/**
	* 授权
	*/
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("doGetAuthorizationInfo=>进行了授权逻辑");
        return null;
    }

	/**
	* 认证
	*/
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("doGetAuthenticationInfo=>进行了认证逻辑");
        UsernamePasswordToken userToken = (UsernamePasswordToken) token;
        User user = userService.findByName(userToken.getUsername());
        //当前realm对象的name
        String realmName = getName();
        //盐值
        ByteSource credentialsSalt = null;
        if (user == null) {
            //抛出异常  UnknownAccountException
            return null;
        }else{
            //盐值
            credentialsSalt = ByteSource.Util.bytes(user.getSalt());
        }

        return new SimpleAuthenticationInfo(user,user.getPwd(),credentialsSalt, realmName);
    }
}

doGetAuthenticationInfo 方法中,从token中获取user对象后,从user对象中取出存在数据库中的 salt =>盐值,在登陆时,会拿到数据库中的密码,与该账户所存储的盐值,交给shiro管理。

SimpleAuthenticationInfo(
user, //用户对象
user.getPwd(),  // 用户的密码
credentialsSalt, //盐值
realmName   //当前realm对象的name
);

用户登录

 /**
     * 登录
     *
     * @param user
     * @return
     */
    @RequestMapping("/login")
    public ResultUtil login(User user) {

        //获取当前用户
        Subject subject = SecurityUtils.getSubject();
         //将用户输入的用户名写密码封装到一个UsernamePasswordToken对象中
        UsernamePasswordToken token = new UsernamePasswordToken(user.getAccount(), user.getPwd());
        
		//用Subject对象执行登录方法,没有抛出任何异常说明登录成功
        try {
            //执行登录方法,如果没有异常,就OK
            //login()方法会调用 Realm类中执行认证逻辑的方法,
            //并将这个参数传递给doGetAuthenticationInfo()方法
            subject.login(token);
            return new ResultUtil(0, token, "登录成功!");
        } catch (UnknownAccountException e) {
            return new ResultUtil(-1, null, "账号错误!");
        } catch (IncorrectCredentialsException e) {
            return new ResultUtil(-1, null, "密码错误!");
        }
    }

用户注册

/**
     * 注册
     *
     * @param user
     * @return
     */
    @RequestMapping("/register")
    public ResultUtil register(User user) {
        String msg = userService.addUser(user);
        if ("134".equals(msg)){
            return new ResultUtil(-1,null,"该账户已被注册!");
        }else if ("135".equals(msg)){
            return new ResultUtil(-1,null,"该昵称已存在!");
        }else if ("502".equals(msg)){
            return new ResultUtil(-1,null,"注册失败!");
        }else if ("200".equals(msg)){
            return new ResultUtil(0,null,"注册成功!");
        }else {
            return new ResultUtil(-1,null,"未知错误!");
        }
    }
注册的服务接口实现类
@Override
    public String addUser(User user) {
        //查询注册的用户名,在数据库中是否存在,如果查询到的信息为空,则注册添加用户信息
        User info = userMapper.findByName(user.getAccount());
        //查询注册的昵称,在数据库中是否存在
        User byNickName = userMapper.findByNickName(user.getNickname());
        if(info != null){
            return "134";
        }else if(byNickName != null){
            return "135";
        }else{
            try {
                //随机生成UUID,作为盐值
                String uuid = UUID.randomUUID().toString().replace("-", "");
                //调用MD5_Shiro工具类,生成加密后的密码
                String newPwd = MD5_Shiro.encryptPassword("MD5",user.getPwd(),uuid,1024);
                user.setPwd(newPwd);
                user.setSalt(uuid);
                userMapper.addUser(user);
                return "200";
            } catch (Exception e) {
                e.printStackTrace();
                return "502";
            }
        }
    }

在用户注册的时候,会从数据库中查找 账号和使用的昵称 是否已存在,保持账号和昵称,是唯一的;
加密使用的盐值,使用了UUID随机生成的,然后在注册存入数据库时,一同存入;

MD5_Shiro.encryptPassword(
"MD5",  //使用MD5加密方式
user.getPwd(),    //用户输入的密码
uuid,             //生成的UUID编码,作为加密使用的盐值
1024              //加密次数
);

MD5_Shiro工具类

public class MD5_Shiro {
    /**
     * 随机生成 salt 需要指定 它的字符串的长度
     *
     * @param len 字符串的长度
     * @return salt
     */
    public static String generateSalt(int len) {
        //一个Byte占两个字节
        int byteLen = len >> 1;
        SecureRandomNumberGenerator secureRandom = new SecureRandomNumberGenerator();
        return secureRandom.nextBytes(byteLen).toHex();
    }

    /**
     * 获取加密后的密码,使用默认hash迭代的次数 1 次
     *
     * @param hashAlgorithm hash算法名称 MD2、MD5、SHA-1、SHA-256、SHA-384、SHA-512、etc。
     * @param password      需要加密的密码
     * @param salt          盐
     * @return 加密后的密码
     */
    public static String encryptPassword(String hashAlgorithm, String password, String salt) {
        return encryptPassword(hashAlgorithm, password, salt, 1);
    }

    /**
     * 获取加密后的密码,需要指定 hash迭代的次数
     *
     * @param hashAlgorithm  hash算法名称 MD2、MD5、SHA-1、SHA-256、SHA-384、SHA-512、etc。
     * @param password       需要加密的密码
     * @param salt           盐
     * @param hashIterations hash迭代的次数
     * @return 加密后的密码
     */
    public static String encryptPassword(String hashAlgorithm, String password, String salt, int hashIterations) {
        SimpleHash hash = new SimpleHash(hashAlgorithm, password, salt, hashIterations);
        return hash.toString();
    }
}

User实体类

@Data
public class User {
    private Integer uId;
    private String account;
    private String pwd;
    private String nickname;
    private String salt;
    private Date createTime;
}

UserMapper.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.sys.mapper.UserMapper">
    <resultMap id="user-map" type="user">
        <id column="u_id" property="uId"/>
        <result column="account" property="account"/>
        <result column="pwd" property="pwd"/>
        <result column="nickname" property="nickname"/>
        <result column="salt" property="salt"/>
        <result column="create_time" property="createTime"/>
    </resultMap>

    <select id="findByName" parameterType="String" resultMap="user-map">
        select * from user where account = #{account}
    </select>

    <select id="findByNickName" parameterType="String" resultType="user">
        select * from user where nickname = #{nickname}
    </select>

    <insert id="addUser" parameterType="user">
        insert into user(account, pwd, nickname, salt) values (#{account},#{pwd},#{nickname},#{salt})
    </insert>
</mapper>

数据库结构

在这里插入图片描述

其余的mapper层接口和service层接口,这里就不附加上了,以上这些就是,我自己找的资料,总结后实现了简单的登录和注册,以及加密的功能,如有什么问题,可以在评论区指出。

  • 2
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值