1 认证
Spring Security认证分为两种:表单认证(如用户名、密码),HTTP基础认证,就是把账号信息放到请求头。
1.1 认证过程
认证过程如下图,我们可以按照下图来编写认证的代码,构建Authentication是框架帮我做的,我们接着往下写代码。
1.2 表单认证
1.2.1 配置AuthenticationManager
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
SysUserRepository sysUserRepository;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(new CusotmUserDetailsService(sysUserRepository));
}
}
1.2.2 编写UserDetailsService
UserDetailsService依赖于UserDetailsRepository,UserDetailsResposity依赖于SysUser对象。
用户的实体:
@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
public class SysUser implements UserDetails {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String realName;
@Column(unique = true)
private String username;
private String password;
private String role;
public SysUser(String realName, String username, String password, String role) {
this.realName = realName;
this.username = username;
this.password = password;
this.role = role;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
@Override
public String getPassword() {
return this.password;
}
@Override
public String getUsername() {
return this.username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
用户的Repository:
public interface SysUserRepository extends JpaRepository<SysUser, Long> {
Optional<SysUser> findByUsername(String username);
}
UserDetailsService:
public class CusotmUserDetailsService implements UserDetailsService {
SysUserRepository sysUserRepository;
public CusotmUserDetailsService(SysUserRepository sysUserRepository) {
this.sysUserRepository = sysUserRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Optional<SysUser> sysUserOptional = sysUserRepository.findByUsername(username);
return sysUserOptional
.orElseThrow(() -> new UsernameNotFoundException("Username not found"));
}
1.2.3 编写控制器测试
@GetMapping("/")
public String hello(){
return "Hello Spring Security";
}
1.3 HTTP基础认证
HTTP基础认证过程和表单认证过程机器相似,唯一修改的地方就是添加一个新的HTTP基础认证的AuthenticationManager。
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/everyCanAccess").permitAll()
.and()
.httpBasic().authenticationEntryPoint(authenticationEntryPoint());
}
在请求头上添加账户信息,信息为Basic+用户名:密码的Base64编码
1.4 密码编码
在webSecurityConfig添加编码容器
@Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
2 授权
授权分为两种:用户角色授权和方法授权
2.1 授权过程
认证完成之后进行授权,授权需要从数据库中查询当前认证用户所属的角色,然后根据角色的权限,判断该用户是否有权限访问资源。
2.2 用户授权
2.2.1 配置Web路径的安全访问
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
SysUserRepository sysUserRepository;
/* @Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(new CusotmUserDetailsService(sysUserRepository));
}*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/everyCanAccess").permitAll()
.antMatchers("/authenticatedCanAccess").authenticated()
.antMatchers("/adminCanAccess").hasRole("ADMIN")
.antMatchers("/userCanAccess").access("hasRole('USER') or hasRole('ADMIN')")
.antMatchers("/threeCanAccess").access("@webSecurity.checkUsernameLenEq3(authentication)")
.anyRequest().authenticated()
.and()
.httpBasic().authenticationEntryPoint(authenticationEntryPoint());
}
@Bean
UserDetailsService userDetailsService(SysUserRepository sysUserRepository){
return new CusotmUserDetailsService(sysUserRepository);
}
@Bean
AuthenticationEntryPoint authenticationEntryPoint(){
BasicAuthenticationEntryPoint authenticationEntryPoint = new BasicAuthenticationEntryPoint();
authenticationEntryPoint.setRealmName("wisely");
return authenticationEntryPoint;
}
@Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
配置属性总共包括以下几种:
- access(String) 如果给定的SpEL表达式计算结果为true,就允许访问
- anonymous() 允许匿名用户访问
- authenticated() 允许认证的用户进行访问
- denyAll() 无条件拒绝所有访问
- fullyAuthenticated() 如果用户是完整认证的话(不是通过Remember-me功能认证的),就允许访问
- hasAuthority(String) 如果用户具备给定权限的话就允许访问
- hasAnyAuthority(String…)如果用户具备给定权限中的某一个的话,就允许访问
- hasRole(String) 如果用户具备给定角色(用户组)的话,就允许访问
- hasAnyRole(String…) 如果用户具有给定角色(用户组)中的一个的话,允许访问.
- hasIpAddress(String 如果请求来自给定ip地址的话,就允许访问.
- not() 对其他访问结果求反.
- permitAll() 无条件允许访问
- rememberMe() 如果用户是通过Remember-me功能认证的,就允许访问
2.2.2 重写SysUser
重写UserDetails的getAuthorities方法或缺的用户角色
@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
public class SysUser implements UserDetails {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String realName;
@Column(unique = true)
private String username;
private String password;
private String role;
public SysUser(String realName, String username, String password, String role) {
this.realName = realName;
this.username = username;
this.password = password;
this.role = role;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();
SimpleGrantedAuthority authority = new SimpleGrantedAuthority(this.role);
authorities.add(authority);
return authorities;
}
@Override
public String getPassword() {
return this.password;
}
@Override
public String getUsername() {
return this.username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
2.2.3 编写控制器
@RestController
public class IndexController {
@GetMapping("/")
public String hello(){
return "Hello Spring Security";
}
@GetMapping("/user")
public Map<String, Object> getUserInfo(@AuthenticationPrincipal SysUser sysUser,
@CurrentSecurityContext SecurityContext securityContext,
@CurrentSecurityContext(expression = "authentication") Authentication authentication ){
SecurityContext context = SecurityContextHolder.getContext();
Authentication auth = context.getAuthentication();
Object principal = auth.getPrincipal();
Object details = auth.getDetails();
Map<String, Object> map = new HashMap<>();
map.put("sysUser", sysUser);
map.put("authentication", authentication);
map.put("principal", principal);
map.put("details", details);
return map;
}
@GetMapping("/everyCanAccess")
public String everyCanAccess(){
return "任何用户可访问";
}
@GetMapping("/authenticatedCanAccess")
public String authenticatedCanAccess(){
return "任何登录用户可访问";
}
@GetMapping("/userCanAccess")
public String userCanAccess(){
return "角色为ROLE_USER或ROLE_ADMIN的用户都可访问";
}
@GetMapping("/adminCanAccess")
public String adminCanAccess(){
return "角色为ROLE_ADMIN用户可访问";
}
@GetMapping("/threeCanAccess")
public String threeCanAccess(){
return "只有用户名字符串长度为3的用户可以访问";
}
}
测试:
2.3 方法授权
2.3.1 开启方法授权
@EnableGlobalMethodSecurity(prePostEnabled = true)
2.3.2 方法授权的不同注解
- @PreAuthorize --适合进入方法之前验证授权
- @PostAuthorize --检查授权方法之后才被执行
- @PostFilter --在方法执行之后执行,而且这里可以调用方法的返回值,然后对返回值进行过滤或处理或修改并返回
- @PreFilter --在方法执行之前执行,而且这里可以调用方法的参数,然后对参数值进行过滤或处理或修改
2.3.3 给方法授权
@RestController
public class IndexController {
@GetMapping("/")
public String hello(){
return "Hello Spring Security";
}
@GetMapping("/user")
public Map<String, Object> getUserInfo(@AuthenticationPrincipal SysUser sysUser,
@CurrentSecurityContext SecurityContext securityContext,
@CurrentSecurityContext(expression = "authentication") Authentication authentication ){
SecurityContext context = SecurityContextHolder.getContext();
Authentication auth = context.getAuthentication();
Object principal = auth.getPrincipal();
Object details = auth.getDetails();
Map<String, Object> map = new HashMap<>();
map.put("sysUser", sysUser);
map.put("authentication", authentication);
map.put("principal", principal);
map.put("details", details);
return map;
}
@GetMapping("/everyCanAccess")
public String everyCanAccess(){
return "任何用户可访问";
}
@GetMapping("/authenticatedCanAccess")
public String authenticatedCanAccess(){
return "任何登录用户可访问";
}
@GetMapping("/userCanAccess")
public String userCanAccess(){
return "角色为ROLE_USER或ROLE_ADMIN的用户都可访问";
}
@GetMapping("/adminCanAccess")
public String adminCanAccess(){
return "角色为ROLE_ADMIN用户可访问";
}
@GetMapping("/threeCanAccess")
public String threeCanAccess(){
return "只有用户名字符串长度为3的用户可以访问";
}
@GetMapping("/methodAdmin")
@PreAuthorize("hasRole('ADMIN')")
public String methodAdmin(){
return "只有角色为ROLE_ADMIN的用户可访问";
}
@GetMapping("/methodDiffName")
@PreAuthorize("#user.username != authentication.name")
public String methodDiffName(@RequestBody SysUser user){
return "传输的用户名和当前用户名不相同的可访问";
}
@GetMapping("/methodNameThree")
@PreAuthorize("@webSecurity.checkUsernameLenEq3(authentication)")
public String methodNameThree(){
return "只有用户名字符串长度为3的用户可以访问";
}
@GetMapping("/methodAnotherName3")
@PostAuthorize("returnObject.length() == 5")
public String anotherTree(@AuthenticationPrincipal SysUser sysUser){
return "Hi" + sysUser.getUsername();
}
@GetMapping("/methodFilterIn")
@PreAuthorize("hasRole('USER')")
@PreFilter("filterObject%2 == 0")
public List<Integer> methodFilterIn (@RequestParam List<Integer> numbers){
return numbers;
}
@GetMapping("/methodFilterOut")
@PostFilter("hasRole('USER') and filterObject%2 == 0")
public Integer[] methodFilterIn (){
Integer[] numbers = {1,2,3,4,5,6,7,8,9};
return numbers;
}
}