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

您所在的位置:网站首页 前后端分离登录注册失败 springboot+shiro 实现登录、注册和密码加密(vue+springboot前后端分离)

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

2024-07-14 03:53| 来源: 网络整理| 查看: 265

首先引入依赖 org.slf4j slf4j-api 1.7.30 org.mybatis.spring.boot mybatis-spring-boot-starter 2.1.2 org.springframework spring-webmvc 5.2.8.RELEASE org.apache.shiro shiro-spring 1.5.2 com.alibaba druid 1.1.21 log4j log4j 1.2.17 org.springframework.boot spring-boot-starter-web mysql mysql-connector-java 5.1.45 org.projectlombok lombok true

附:数据库配置和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 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 select * from user where account = #{account} select * from user where nickname = #{nickname} insert into user(account, pwd, nickname, salt) values (#{account},#{pwd},#{nickname},#{salt}) 数据库结构

在这里插入图片描述

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



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3