回到序章
来来来,点这
正文
引入依赖
1 2 3 4 5 6 7 8 9 10
| <!-- DB 支持 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency>
|
配置文件加入数据库信息
这里创建一个空的库即可,JPA 会去生成表的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| spring: datasource: url: jdbc:mysql:///db_test?useUnicode=true&useSSL=false&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai username: root password: root jpa: database: mysql database-platform: mysql hibernate: ddl-auto: update show-sql: true properties: hibernate: dialect: org.hibernate.dialect.MySQL8Dialect
|
实体类
角色实体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import lombok.Data;
import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id;
@Data @Entity(name = "t_role") public class SysRole { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String code; private String name; }
|
用户信息实体,需要实现 UserDetails
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| import lombok.Data; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails;
import javax.persistence.*; import java.util.ArrayList; import java.util.Collection; import java.util.List;
@Data @Entity(name = "t_user") public class SysUser implements UserDetails { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(unique = true) private String username; private String password; private String nickName;
@Column(columnDefinition = "boolean default true") private boolean locked;
@Column(columnDefinition = "tinyint default 1") private int status;
@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST) private List<SysRole> roles;
@Override public Collection<? extends GrantedAuthority> getAuthorities() { List<SimpleGrantedAuthority> authorities = new ArrayList<>(); roles.forEach(r -> authorities.add(new SimpleGrantedAuthority("ROLE_" + r.getCode()))); return authorities; }
@Override public boolean isAccountNonExpired() { return true; }
@Override public boolean isCredentialsNonExpired() { return true; }
@Override public boolean isAccountNonLocked() { return locked; }
@Override public boolean isEnabled() { return status == 1; } }
|
dao 类
1 2 3 4 5 6
| import org.springframework.data.jpa.repository.JpaRepository;
public interface UserDao extends JpaRepository<SysUser, Long> {
SysUser findSysUserByUsername(String username); }
|
service 层
因为是要替换原来的认证,所以要实现 UserDetailsService。 重写里面的 loadUserByUsername(),这个方法的参数就是用户在登录的时候传入的用户名,根据用户名去查询用户信息(查出来之后,系统会自动进行密码比对)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| 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;
@Service public class UserLoginService implements UserDetailsService {
@Autowired private UserDao userDao;
@Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { SysUser user = userDao.findSysUserByUsername(username); if (user == null) { throw new UsernameNotFoundException("账号不存在"); } return user; } }
|
controller 层
增加接口,用于测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController;
@RestController public class TestController {
@GetMapping("/") public String index() { return "index"; }
@GetMapping("/test/{id}") public String test(@PathVariable String id) { return "test" + id; } }
|
更新配置类
主要是配置用刚才写的 service 进行认证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; 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.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.factory.PasswordEncoderFactories; import org.springframework.security.crypto.password.DelegatingPasswordEncoder; import org.springframework.security.crypto.password.NoOpPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration public class SecurityWebConfiguration extends WebSecurityConfigurerAdapter {
@Autowired private UserLoginService userLoginService;
private PasswordEncoder passwordEncoder() { DelegatingPasswordEncoder encoder = (DelegatingPasswordEncoder) PasswordEncoderFactories.createDelegatingPasswordEncoder(); encoder.setDefaultPasswordEncoderForMatches(NoOpPasswordEncoder.getInstance()); return encoder; }
@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth .userDetailsService(userLoginService) .passwordEncoder(passwordEncoder()) ; }
@Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/test/111").hasRole("test1") .antMatchers("/test/222").hasRole("test2") .antMatchers("/test/333").hasAuthority("ROLE_test3") .antMatchers("/test/444").hasAuthority("ROLE_test4") .anyRequest().authenticated() .and() .formLogin() ; } }
|
写个测试类,弄一些测试数据
直接执行即可,这样表和账号都会生成好了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
| import cn.qingmg.demoboot.dao.UserDao; import cn.qingmg.demoboot.entity.SysRole; import cn.qingmg.demoboot.entity.SysUser; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest;
import java.util.ArrayList; import java.util.List;
@SpringBootTest class DemobootApplicationTests {
@Autowired private UserDao userDao;
@Test void contextLoads() { SysUser u1 = new SysUser(); u1.setUsername("user1"); u1.setPassword("123"); u1.setNickName("测试账号1"); u1.setLocked(true); u1.setStatus(1); List<SysRole> rs1 = new ArrayList<>(); SysRole r1 = new SysRole(); r1.setCode("test1"); r1.setName("test1角色"); rs1.add(r1); u1.setRoles(rs1); userDao.save(u1); SysUser u2 = new SysUser(); u2.setUsername("user2"); u2.setPassword("123"); u2.setNickName("测试账号2"); u2.setLocked(true); u2.setStatus(1); List<SysRole> rs2 = new ArrayList<>(); SysRole r2 = new SysRole(); r2.setCode("test2"); r2.setName("test2角色"); rs2.add(r2); u2.setRoles(rs2); userDao.save(u2);
SysUser u3 = new SysUser(); u3.setUsername("user3"); u3.setPassword("123"); u3.setNickName("测试账号3"); u3.setLocked(true); u3.setStatus(1); List<SysRole> rs3 = new ArrayList<>(); SysRole r3 = new SysRole(); r3.setCode("test3"); r3.setName("test3角色"); rs3.add(r3); u3.setRoles(rs3); userDao.save(u3);
SysUser u4 = new SysUser(); u4.setUsername("user4"); u4.setPassword("123"); u4.setNickName("测试账号4"); u4.setLocked(true); u4.setStatus(1); List<SysRole> rs4 = new ArrayList<>(); SysRole r4 = new SysRole(); r4.setCode("test4"); r4.setName("test4角色"); rs4.add(r4); u4.setRoles(rs4); userDao.save(u4); }
}
|
测试
启动项目
访问 http://localhost:8080/ 会自动跳转进 /login
测试内容
- 依次登陆 user1 - user4,分别请求
/test/111 ,/test/222 ,/test/333 ,/test/444 ,/test/555
- 进入数据库表 t_user_roles, 增加 t_user_id = 1, role_id = 4,再测试一次
预期效果
/test/555 任一用户都能请求,而 user1 只能请求 /test/111 而不能请求 /test/222 ,/test/333 ,/test/444 三个接口, user2 只能请求 /test/222 …
- 更新数据库后,user1 可以请求
/test/444 , 其他没有变化。