Repository
- repository是用来定义数据的相关操作的,比如根据id找到对应的用户
- 有了jpa以后,我们可以仅仅使用接口定义的方式就能够完成数据集的增删查改
定义一个Repository
- 首先要在项目中创建一个repository的包,并且在里面创建一个user包
- 再在其中创建一个UserRepository接口,代码如下
package kmhc.repository.user;
import java.util.Optional;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import kmhc.domain.user.User;
@Repository
public interface UserRepository extends CrudRepository<User, Long> {
Optional<User> findByUsername(String username);
}
- 对上面的代码进行分析
- @Repository表明这是一个repository,将其添加到spring上下文管理之中
- 接口继承了CrudRepository<User, Long>,这里的User指我们定义的领域类,表明是谁的仓库,Long代表了主键的类型
- 这只是一个接口,但是足够了,因为jpa会在运行时自动帮我们实现接口的实现类,用到了动态代理的技术
- 其它领域类的接口也是同理,下面给出代码
package kmhc.repository.user;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import kmhc.domain.user.Authority;
@Repository
public interface AuthorityRepository extends CrudRepository<Authority, Long> {
}
package kmhc.repository.user;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import kmhc.domain.user.Group;
@Repository
public interface GroupRepository extends CrudRepository<Group, Long> {
}
package kmhc.repository.user;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import kmhc.domain.user.GroupAuthority;
@Repository
public interface GroupAuthorityRepository extends CrudRepository<GroupAuthority, Long> {
}
- 有了repository以后,我们就可以通过@Autowired的方式使用它了
自定义UserDetails
- spring security默认的UserDetails只包含了username,password,enabled等基本信息,并不包含我们扩展的信息,所以我们需要对UserDetails进行扩展
- 具体的做法是实现UserDetailsService,并且重写里面的loadUserByUsername方法
- 首先在项目中创建一个名叫security的包
- 然后创建一个类,实现UserDetailsService,代码如下
package kmhc.security;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import kmhc.domain.user.User;
import kmhc.repository.user.UserRepository;
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepo;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Optional<User> optional = userRepo.findByUsername(username);
if (optional.isPresent()) {
return optional.get();
}
throw new UsernameNotFoundException("User '" + username + "' not found!");
}
}
- 对上面的代码进行分析
- @Service表明它是一种服务,注册到spring的上下文管理之中
- @Autowired用于自动注入,注意这里注入的是Bean,它和普通的new一个类是有很大区别的
- 实现UserDeailsService接口,并且重写了loadUserByUsername()方法,从这里的方法名就可以看出spring security是根据username查找用户的,所以把username当成主键也是一种不错的选择,否则需要在username上建立索引以提高查找效率
- 方法内部调用了UserRepository的findByUsername()的方法,UserRepository里面有几个默认的方法,我们不需要重写
- Optional类可以有效地避免在第一次没找到username的情况下就报错,而是会返回一个null,可以使用optional.isPresent()来判断用户是否存在
- 不存在就抛出UsernameNotFoundException异常,并自定义错误信息
Spring Security的具体配置
- 有了自定义的UserDetails后,就可以将它当作spring security身份验证中的默认配置了
- 先给出Spring Security配置的代码
package kmhc.security;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomUserDetailsService userDetailsService;
@Autowired
private DataSource dataSource;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService)
.passwordEncoder(encoder());
auth
.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery(
"select username, password, enabled from users where username = ?"
)
.authoritiesByUsernameQuery(
"select username, authority from authorities where user_id in (select user_id from users where username = ?)"
)
.groupAuthoritiesByUsername(
"select g.id, g.group_name, ga.authority from user_groups g, group_members gm, group_authorities ga " +
"where gm.user_id in (select user_id from users where username = ?) and g.id = ga.group_id and g.id = gm.group_id"
);
}
@Bean
public PasswordEncoder encoder() {
return new BCryptPasswordEncoder();
}
}
- 对上面的代码进行分析
- 自动注入了自定义的UserDetailsService和DataSource,这里DataSource直接注入即可,没有任何其它修改
- 重写了configure()方法,对参数auth进行设置,调用userDetailsService()设置为我们自定义的UserDetailsService;调用passwordEncoder,设置密码加密解密为我们自定义的PasswordEncoder,注意这是一个Bean,会加入到spring的上下文管理之中
- 还设置用户存储模式为jdbc存储,设置datasource并且修改了三条查询语句
- 以上几点配置完毕后,Spring Security就可以正式的投入工作了,至少在权限管理方面能够正常的工作
至此,对repository的配置和spring security的权限配置已讲解完毕,下一节就开始正式地进行restful api的开发了,并且使用postman工具对后端接口测试