Spring Security其实功能十分强大,几乎可以支撑所有近年来互联网产品的登录认证和授权功能。个人认为,全面了解Spring Security是了解Spring体系的一个重要的基础
Spring Security的核心功能是两个非常哲学的点:
- 认证(你是谁)
- 授权(你能做什么)
个人认为,认证和授权其实是可以分开的两个领域。举个例子,在大型企业的内部应用中,员工的认证往往是集中在一套核心的用户信息管理系统中。这套用户管理系统能够为所有的企业内部员工进行认证,并且可以进行实时更新以便了解员工的信息变更情况,一般来说这里可以引入LDAP。
当然,企业内部的应用很多,且每个应用中,每个层级的用户能够使用的应用功能也因人而异,所以这里就引入了授权。每个应用的授权可以和认证进行剥离,因为针对不同的应用,只有应用开发者或管理员最了解该应用内每个用户的权限和对应功能,就好比每个人都有身份号,但身份号所关联的银行卡、驾驶证、社保卡分别可以在不同的领域进行使用而互不关联,且归不同的有关部门进行管理。
也就是说,用户的认证可以进行统一的管理,而用户的授权则需要针对对应的应用来规定,应用与应用之间的授权需要解耦。当然,一般来说,统一的身份认证系统也可以进行授权。
接下来是干货
自定义用户认证
如果是单独的一套用户体系,那我们需要自定义自己的认证逻辑以及登录界面。Spring Security中对于用户认证的定制化开发,只需要直接继承WebSecurityConfigurerAdapter这个类,并重写其中的各个configure方法,在通过启动时的自动注入,就可以完成认证的定制化开发。
@Configuration
public class BrowerSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin() // 定义:当需要用户登录时候,转到的登录页面。
.and()
.authorizeRequests() // 定义:哪些URL需要被保护、哪些不需要被保护
.anyRequest() // 任何请求,登录后可以访问
.authenticated();
}
}
有了登录的处理后,我们需要配置用户认证的逻辑,这里需要用到了UserDetailsService这个接口,该接口为处理用户登录逻辑的主要接口类,需要对立面的loadUserByUsername这个方法进行重写,即可实现定制化的丁路逻辑。
@Component
public class MyUserDetailsService implements UserDetailsService {
private Logger logger = LoggerFactory.getLogger(getClass());
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
logger.info("用户的用户名: {}", username);
// TODO 根据用户名,查找到对应的密码,与权限
// 封装用户信息,并返回。参数分别是:用户名,密码,用户权限
User user = new User(username, "123456",
AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
return user;
}
}
这里需要特别提到的是,loadUserByUsername方法的返回类型是UserDetails,其实说白了就是一个基本的用户属性接口类,其中最重要的属性有三:用户名,密码,用户权限,类型为string,string,Collection<>。
在loadUserByUsername的方法中,如何获取这三个属性的方法就应需求而定了。这里可以是本系统的用户体系,也可以是通过远程调用与其他用户系统的远程获取,更可以是认证和授权的完全解耦(用户名和密码远程获取,用户权限本地定义)。
加密和验证
Spring Security中有一个PasswordEncoder接口,该接口实现后,需要使用时可直接注入到对应的对象中,并正在loadUserByUsername方法中使用,从而对用户密码password进行加密和验证操作
public interface PasswordEncoder {
// 对密码进行加密
String encode(CharSequence var1);
// 对密码进行判断匹配
boolean matches(CharSequence var1, String var2);
}
PasswordEncoder为一个接口类,可对这个加密方式进行最基本的实现,如Spring自带的BCryptPasswordEncoder类
// BrowerSecurityConfig
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
经过加密后的loadUserByUsername如下
@Component
public class MyUserDetailsService implements UserDetailsService {
private Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
logger.info("用户的用户名: {}", username);
String password = passwordEncoder.encode("123456");
logger.info("password: {}", password);
// 参数分别是:用户名,密码,用户权限
User user = new User(username, password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
return user;
}
}
原文链接:https://blog.csdn.net/u013435893/article/details/79596628
基本功能配置
了解以上几点以后,可以搭建一个具备基本认证能力的Spring Security服务,该服务具备认证和授权的能力,且可通过简单的配置完成不同路径对应的认证策略,即某些路径可直接访问,某些路径在未登录的情况下必须先完成认证和授权才能访问
// BrowerSecurityConfig.java
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin() // 定义当需要用户登录时候,转到的登录页面。
.loginPage("/login.html") // 设置登录页面
.loginProcessingUrl("/user/login") // 自定义的登录接口
.and()
.authorizeRequests() // 定义哪些URL需要被保护、哪些不需要被保护
.antMatchers("/login.html").permitAll() // 设置所有人都可以访问登录页面
.anyRequest() // 任何请求,登录后可以访问
.authenticated()
.and()
.csrf().disable(); // 关闭csrf防护
}
区别网页和请求的授权方式
一般来说,网页的跳转和直接通过前后端、restful api进行的跳转需要区分开,而区分的方式应该在对应的controller接口中进行处理。简单的处理方式就是通过判断请求来自的路径中,指向的后缀是否为.html,但这里情况可能不同,现在的大部分web网站也不是简单的.html文件进行开发,所以可以直接通过Http请求中的User-Agent进行进一步识别,定位用户使用的终端,再在接口内进行相应的判断
网友评论