spring-boot-security-login
UserDetailsService
用于登陆时获取用户信息login.sql
@Entity
@Table(name = "user_account")
public class User {
@Id
@Column(unique = true, nullable = false)
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
private String email;
@Column(length = 60)
private String password;
private boolean enabled;
private boolean isUsing2FA;
private String secret;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "users_roles", joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"))
private Collection<Role> roles;
@Entity
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@ManyToMany(mappedBy = "roles")
private Collection<User> users;
@ManyToMany
@JoinTable(name = "roles_privileges", joinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "privilege_id", referencedColumnName = "id"))
private Collection<Privilege> privileges;
private String name;
@Entity
public class Privilege {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
@ManyToMany(mappedBy = "privileges")
private Collection<Role> roles;
UserDetailsService
接口loadUserByUsername
方法,返回User
给security进行验证User
构造方法,依次是账号,密码,账号是否过期,证书是否过期,是否锁定账号,权限集合@Service("userDetailsService")
@Transactional
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
public MyUserDetailsService() {
super();
}
// API
@Override
public UserDetails loadUserByUsername(final String email) throws UsernameNotFoundException {
try {
final User user = userRepository.findByEmail(email);
if (user == null) {
throw new UsernameNotFoundException("No user found with username: " + email);
}
return new org.springframework.security.core.userdetails.User(user.getEmail(), user.getPassword(), user.isEnabled(), true, true, true, getAuthorities(user.getRoles()));
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
// UTIL
private final Collection<? extends GrantedAuthority> getAuthorities(final Collection<Role> roles) {
return getGrantedAuthorities(getPrivileges(roles));
}
private final List<String> getPrivileges(final Collection<Role> roles) {
final List<String> privileges = new ArrayList<>();
final List<Privilege> collection = new ArrayList<>();
for (final Role role : roles) {
collection.addAll(role.getPrivileges());
}
for (final Privilege item : collection) {
privileges.add(item.getName());
}
return privileges;
}
private final List<GrantedAuthority> getGrantedAuthorities(final List<String> privileges) {
final List<GrantedAuthority> authorities = new ArrayList<>();
for (final String privilege : privileges) {
authorities.add(new SimpleGrantedAuthority(privilege));
}
return authorities;
}
}
@Autowired
private MyUserDetailsService userDetailsService;
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
prePostEnabled
设置为true@EnableGlobalMethodSecurity(prePostEnabled = true)
@Configuration
public class SecurityJavaConfig extends WebSecurityConfigurerAdapter {
@RestController
@RequestMapping("/api/user")
public class BusinessController {
@DeleteMapping("/{id}")
@PreAuthorize("hasAuthority('DELETE_USER')")
public String deleteUser(@PathVariable Long id){
return "delete user success by user id :"+id;
}
@GetMapping("/{id}")
@PreAuthorize("hasAuthority('GET_USER')")
public User getUser(@PathVariable Long id){
User user = new User();
user.setId(id);
return user;
}
}
@Test
public void notLogin() {
ResponseEntity<String> entity = this.restTemplate.exchange("http://localhost:8081/api/user/1", HttpMethod.DELETE,
new HttpEntity<Void>(loginHeaders), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
}
@Test
public void noHasDeleteUserAuthority() {
MultiValueMap<String, String> form = new LinkedMultiValueMap<>();
form.set("username", "[email protected]");
form.set("password", "123456");
login(form);
ResponseEntity<String> entity = this.restTemplate.exchange("http://localhost:8081/api/user/1", HttpMethod.DELETE,
new HttpEntity<Void>(loginHeaders), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
}
configure(HttpSecurity http)
中添加rememberMe.and().rememberMe().tokenValiditySeconds(60*60*24);
server:
port: 8081
tomcat:
uri-encoding: UTF-8
servlet:
session:
timeout: 1m
remember-me
:true
,此时返回值会生成remember-me的cookie值,存储了账号的md5加密和过期时间,如下remember-me=dGVzdCU0MHRlc3QuY29tOjE1NjY4MzE1NjQzNjQ6YjNiODE4YzhhZTgwMDMwNzY4NDE2YTE1ZDU5YmZmOTg; Max-Age=30000; Expires=Mon, 26-Aug-2019 14:59:24 GMT; Path=/; HttpOnly
JSESSIONID=1DD1D2BDFDA29944732B394F26F73D7E; Path=/; HttpOnly
@Test
public void deleteOneMinuteLaterByJsessionId() throws InterruptedException, IOException {
HttpHeaders loginHeaders = getHttpHeaders(1);
//sleep one minute until session expired
Thread.sleep(60000L);
ResponseEntity<String> entity = this.restTemplate.exchange("http://localhost:8081/api/user/1", HttpMethod.DELETE,
new HttpEntity<Void>(loginHeaders), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
}
@Test
public void deleteByRememberMeCookie() throws IOException {
HttpHeaders loginHeaders = getHttpHeaders(0);
ResponseEntity<String> entity = this.restTemplate.exchange("http://localhost:8081/api/user/1", HttpMethod.DELETE,
new HttpEntity<Void>(loginHeaders), String.class);
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK);
}
上一篇:Spring Security 对Rest风格API的保护