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(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
;
}
}