SpringSecurity框架-01
1.框架简介
2.SpringSecurity入门案例
3.SpringSecurity权限方案
4.SpringSecurity微服务权限方案
5.SpringSecurity原理总结
1.1概要
SpringSecurity提供了一套Web应用安全性的完整解决方案
(1)认证 (2)授权
和Shiro的比较 Shiro功能不如SpringSecurity强大 但是通用性更强 配置简单
一般搭配是SSM+Shiro
SpringBoot+SpringSecurity
1.2入门案例
在Security中默认用户名是user 默认密码是enerated security password
1.3基本原理
SpringSecurity本质上是一个过滤器链
①FilterSecurityInterceptor 是一个方法级的权限过滤器,基本位于过滤链的最底部
②ExceptionTranslationInterceptor 异常过滤器
③UsernamePasswordAuthenticationFilter 对/login的POST请求做拦截,校验表单中的用户名,密码
自定义逻辑 UserDetailService PasswordEncoder 两个接口
①UserDetailService 查数据库
创建类继承UsernamePasswordAuthenticationFilter 重写三个方法
UserDetailService 编写查数据库过程 返回User对象,这个User是SpringSecurity框架定义的对象
②PasswordEncoder 密码加密接口,用于返回User对象里面密码加密
web权限方案
(1)认证 (2)授权
①设置用户名和密码:
1)可以通过配置文件设置: spring.security.user.name = " " spring.security.user.password = " "
2)可以通过配置类实现:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{}
重写 configure(AuthenticationManagerBuilder auth)这个方法
String password=passwordEncoder.encode("{password}")
auth.inMemoryAuthentication().withUser("").password("").roles("");
@Bean
PasswordEncoder password(){
return new BCryptPasswordEncoder();
}
3)自定义实现类实现UserDetailService:
第一步 创建一个配置类,设置使用哪个UserDetailService实现类
第二步 编写接口实现类,返回User对象,User对象有用户名密码和操作权限
1 @Configuration
2 public class SecurityConfigTest extends WebSecurityConfigurerAdapter {
3 @Autowired
4 private UserDetailService userDetailServise;
5 @Override
6 protected void configure(AuthenticationManagerBuilder auth)throws Exception {
7 auth.userDetailsService(userDetailsService).passwordEncoder(password());
8 }
9 @Bean
10 PasswordEncoder password(){
11 return new BCryptPasswordEncoder();
12 }
13 }
自定义一个实现类
@Service("userDetailsService") public class MyUserDetailsService implements UserDetailsService { @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException{ List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("roles"); return new User("{username}","{password}",auths);
}
}
自定义登录实现 前后端分离
SpringSecurity自己就配置好了登录和登出访问的路径 \login \logout 我们也可以在配置类中(HttpSecurity http)中重新配置访问路径
步骤一:
定义一个UserDetialsService的实现类 UserDetialsServiceImpl()
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
UserService userService;
@Autowired
AuthService authService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if(username==null || "".equals(username)){
throw new RuntimeException("用户名不能为空");
}
// 根据用户名查用户
try{
com.kanno.kqclas.entity.User user = userService.findUserByName(username);
List<GrantedAuthority> auths = new ArrayList<>();
List<Auth> authList = authService.findAuthByRoleId(user.getRid());
for(Auth auth:authList){
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(auth.getId()+"");
auths.add(grantedAuthority);
}
return new User(user.getUsername(),new BCryptPasswordEncoder().encode(user.getPassword()),auths);
}catch (Exception e){
return null;
}
}
}
注意,因为SpringSecurity默认返回的是一个User类,所以数据库定义实体类entity的时候最好不要叫User了,否则登录的时候会麻烦
这里返回的User可以配置的不仅仅是用户名和密码,还可以是登录时间、用户状态等,Security会进行更多判断,具体可以查阅官方文档。密码Password一定要经过Encoder,还要和主配置类的Encoder方式一致,
本文这里采用了BCryptEncoder()是一种强校验哈希加密。如果两种加密方式不对,就算是密码一致也无法进行登录。
前端如果使用axios传参,传参默认是把参数放置在请求体当中的,SpringSecurity登录需要在URL中放置参数,可以通过写模板URL来请求。
步骤二:编写SpringSecurity配置类
@Bean
public UserDetailsService userDetailsService(){
return new UserDetailsServiceImpl();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService());
}
@Bean
public BCryptPasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
指定UserDetailServiceImpl实现类,注入到Bean中,如果使用Autowired,则实现类会使用Security默认验证方式。就不会去查数据库了。指定权限认证办法和加密方式。
步骤三:配置configure方法,指定登录成功、失败方法,配置跨域等。
@Override
protected void configure(HttpSecurity http) throws Exception {
//权限校验示例
http.authorizeRequests().antMatchers("/menu").hasAnyAuthority("menu");
//登录请求允许所有权限访问
http.formLogin().permitAll();
//登录验证成功处理函数
http.formLogin().successHandler((request,response,ex)->{
Json json = new Json();
// 验证用户名密码通过,则颁发一个token令牌 token长度为20
String str="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
Random random=new Random();
StringBuffer sb=new StringBuffer();
for(int i=0;i<20;i++){
int number=random.nextInt(62);
sb.append(str.charAt(number));
}
// 保存随机生成的token,保存到redis或者数据库中(最好是redis)
String token = sb.toString();
String username=request.getParameter("username");
System.out.println(username);
try{
User user = userService.findUserByName(username);
user.setToken(token);
Map<String,Object> data = new HashMap<String,Object>();
data.put("id",user.getId()+"");
data.put("rid",user.getRid()+"");
data.put("username",user.getUsername());
data.put("mobile",user.getMobile());
data.put("email",user.getEmail());
data.put("token",token);
userService.loadToken(user);
json.setData(data);
json.setMeta("登录成功!",200);
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
out.write(new ObjectMapper().writeValueAsString(json));
out.flush();
out.close();
}catch (Exception e){
e.printStackTrace();
}
});
// 登录验证失败方法
http.formLogin().failureHandler((request,response,ex)->{
response.setContentType("application/json;charset=utf-8");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
PrintWriter out = response.getWriter();
Json json = new Json();
json.setMeta("登录失败!",403);
out.write(new ObjectMapper().writeValueAsString(json));
out.flush();
out.close();
});
http.cors(); //开启跨域访问
http.csrf().disable(); //放行所有postman测试请求
}
其中跨域请求使用http.cors 开启,需要配置一个名为corsFilter的Bean或者corsConfigurationSource的Bean,来配置跨域请求,这里单独使用cors.disable()不知道为什么没有用...
单独配置一个CorsConfig的@Configuration类
@Configuration
public class CorsConfig {
@Bean
public CorsConfigurationSource corsConfigurationSource(){
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedHeaders(Collections.singletonList("*"));
configuration.setAllowedOriginPatterns(Collections.singletonList("*"));
configuration.setAllowedMethods(Collections.singletonList("*"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
注意此处查看SpringSecurity的源码:

这里会查找@Bean(name="corsFilter")和@Bean(name="corsConfigurationSource")的配置类,当然Spring默认按类注入,不过最好还是按照这个名字写入。
配置好跨域就可以在前端进行登录认证操作了。
这里还引用了Jackson包,使用ObjectMapper进行写入写出,因为Security我们不需要单独写一个RestController了,返回的JSON格式的数据要用ObjectMapper写。
浙公网安备 33010602011771号