Security 认证授权入门教程
简单阐述一下基本的名词:
- 认证:保护服务器的内部资源被合理的请求,只允许那些合理(服务器校验过请求者的身份)能访问,而没有校验过的校验不通过的请求无法访问。
- 授权:请求认证成功后,用来判断该请求是否有权限进行该项工作
单独基于Security作为权限管理框架
Spring Security的基本原理是过滤链,不同的过滤器过滤不同的操作,多个过滤器分工合作组成一套较为完整便与扩展的权限框架(有兴趣的同学可以自己去深入了解)
- maven依赖(Spring 相关的自己加入)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
- 配置 SpringWebSecurityConfigurerAdapter 全局配置中心 大部分中心设置都在里面,(注释写的很详细)
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled=true) //开启权限注解
public class SpringWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
// 配置一个密码加密的算法类 我不太喜欢这么复杂的
// @Bean
// public BCryptPasswordEncoder encoder() {
// return new BCryptPasswordEncoder();
// }
//自己手写了一个密码加密算法,其实没有具体加密功能,但是不写会报错 高版本的security强制要求
@Bean
public PasswordEncoder getPasswordEncoder(){
PasswordEncoder passwordEncoder = new PasswordEncoder() {
@Override
public String encode(CharSequence charSequence) {
//密码加密方式
return charSequence.toString();
}
@Override
public boolean matches(CharSequence charSequence, String s) {
//返回判断用户输入值,和后台校验值是否一致 ,如果此处选择返回true 则用户 密码输入不正确也是可以认证通过
return charSequence.equals(s);
}
};
return passwordEncoder;
}
//自定义用户详情获取类
@Autowired
private SpringUserDetailsService springUserDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 这里是配置初始的用户密码以及对应的角色和权限
// auth.inMemoryAuthentication() //基于内存管理
// .passwordEncoder(encoder()) //密码加密方式
// .withUser("user_1").password("123456").roles("USER")
// .and()
// .withUser("user_2").password("123456").roles("ADMIN").authorities("P1"); //配置用户2 相比用户1多了权限
//设置本地自己实现的用户详情获取对象
auth.userDetailsService(springUserDetailsService).passwordEncoder(getPasswordEncoder());
// auth.authenticationProvider() 该方式是设置本地的 用户密码实现 这个优先级比上面那个高,一旦设置了这个 上面那个不在生效
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// http.csrf().disable() //关闭CSRF
// .requestMatchers()
// //.antMatchers("/oauth/authorize")
// .antMatchers("/index").and().authorizeRequests()///index 需要认证才能访问
// .anyRequest().authenticated() //任何请求都要认证
// .and()
// .formLogin().permitAll(); // 登录接口 完全公开
http.authorizeRequests().anyRequest().authenticated() //任何请求都需要认证
.and().formLogin().permitAll() //表单登录完全公开
.and().logout().permitAll() //退出公开
.and().csrf().disable(); //关闭 CSRF
}
}
- 配置自定义的用户详情获取类 SpringUserDetailsService 用来获取到对应的用户信息。然后返回回去给后面的过滤器使用
@Service
public class SpringUserDetailsService implements UserDetailsService {
//该类只有这一个方法 根据用户名获取用户信息,
//返回的User对象是security自带的对象,第一个参数对象是用户名,第二个是密码,最后一个是权限集合
//如果这里推荐去数据库查询 将结果返回,这里就不展示了。推崇精简
//当用户名称不正确或其他原因可 直接throw UsernameNotFoundException("原因")
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
//传入的s 就是用户名
//LIST 是用户所具备的权限,正规做法是去数据库查询。这里是测试 只添加了一个ROLE_GGG的权限
List<GrantedAuthority> list = new LinkedList();
list.add(new SimpleGrantedAuthority("ROLE_GGG"));
return new User("222","333",list);
//这个写法是这要用户密码栏填写的密码是 333 就能通过验证
//这里的333是我们预测的用户密码,至于校验密码是否正确交由PasswordEncoder.matchesf方法完成
}
}
- 编写需要保护的方法 ,只要在受保护的方法上面加注解
@RestController
public class SpringExampleController {
@GetMapping("/test")
@PreAuthorize("hasAuthority('ROLE_GGG2')") //请求该方法需要拥有权限 ROLE_GGG2
// @PreAuthorize("hasAuthority('ROLE_GGG')") //同上
public String test(){
//当前请求的用户信息,包含了账户密码,权限信息,IP地址等 在业务层任何地方皆可调用
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return "this is test";
}
}
测试
- 首先先尝试直接访问受保护的地址(任意就好了)
-
由于没有登录自动跳转到登录页面,security提供的默认登录页面,更具这个特殊的UserDetailsService,随便填写用户名,密码填写333 就能正确登录。
-
由于没有该页面所以报404 但是认证是通过了
-
该方式是基于Session方式认证不太安全。
-
访问 /test 直接显示没有权限 报403,关闭对应的注解,或者获取用户权限的时候多加权限即可解决
总结:以上都是基于security自带的认证和授权。其中我们只需要负责将用户数据进行填充,和负责管理受保护的资源的权限需求,具体的授权校验和 认证校验security完成,如果你的业务特别复杂/想要深入了解 可以看 AuthenticationProvider(实际完成认证的接口)以及 AccessDecisionManager (默认的实际授权类,认证之后能不能访问受保护的资源全看他了)