可以看到是通过userRepository.findByUsername(username)去获取webGoatUser对象,对象里面就有username,password,role,user这几个元素,其中user是一个对象,后面将获取username,password,还有一些账号状态(过期,冻结等)的元素。
盲猜这个userRepository就是和数据库交互的对象,跟踪过去。
UserRepository.java
package org.owasp.webgoat.users; import org.springframework.data.jpa.repository.JpaRepository; import java.util.List; public interface UserRepository extends JpaRepository<WebGoatUser, String> { WebGoatUser findByUsername(String username); List<WebGoatUser> findAll(); }看到上面的代码,
是接口,没有具体的实现方法,怎么实现的?
又看到JpaRepository这个属于springframework的父类,去找找资料吧。
石器时代 定义数据源,创建JdbcTemplate,然后直接拼接SQL来查询。通用性很差。
工业革命 使用mybatis,定义数据源,定义domain,定义sql映射的xml文件,定义具体操作的DAO层。搭建起来很费时间,后期也经常需要维护。
现代化 用spring-data-jpa,简简单单继承JpaRepository,遵循spring data的规范定义查询接口,无需写实现类就可使用,大大减少数据访问层(DAO)的开发量。
简单来说,接口UserRepository extends JpaRepository下,
声明findByUsername(String username)方法,这个方法名findByUsername是按照规则去设计的,前缀findBy在解析的时候去掉,类似的前缀有:find、read、readBy、get、getBy,然后剩下Username(根据 POJO 规范,首字母变为小写)被判断是否为WebGoatUser对象的属性,是的话就进行查询,最终本质上转换为以下查询:
select u from WebGoatUser u where u.username = ?1由于框架内动态绑定,是不会存在sql注入问题。amazing!非常现代化。
以上介绍的是通过解析方法名创建查询。通过这种方法查询一般不会有sql注入问题。
此外还有:
使用 @Query 创建查询
示范代码:
//使用占位符位置编号 public interface UserDao extends Repository<AccountInfo, Long> { @Query("select a from AccountInfo a where a.accountId = ?1") public AccountInfo findByAccountId(Long accountId); } //使用命名参数来代替占位符位置编号 public interface UserDao extends Repository<AccountInfo, Long> { @Query("from AccountInfo a where a.accountId = :id") public AccountInfo findByAccountId(@Param("id")Long accountId); }以上示范代码都是符合规范,不存在注入。
通过调用 JPA 命名查询语句创建查询
命名查询是 JPA 提供的一种将查询语句从方法体中独立出来,以供多个方法共用的功能。Spring Data JPA 对命名查询也提供了很好的支持。用户只需要按照 JPA 规范在 orm.xml 文件或者在代码中使用 @NamedQuery(或 @NamedNativeQuery)定义好查询语句,唯一要做的就是为该语句命名时,需要满足”DomainClass.methodName()”的命名规则。假设定义了如下接口:
public interface UserDao extends Repository<AccountInfo, Long> { ...... public List<AccountInfo> findTop5(); }