最近看了一些微服务中一些安全认证的代码,感到很懵,很多相关的基础都不是很了解。就想到了大概两年前在慕课网
买的一门课程Spring Security相关的课程,一直没来得及好好学习,借此机会重基础开始学习,并且记录一下学习的
笔记。课程链接如下:
https://coding.imooc.com/learn/list/134.html
1-引入依赖:
使用Spring Security首先需要在项目中引入相关的依赖。我这里创建了一个spring boot的项目。就引入了:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
当我们引入依赖后,什么都不配置的时候,启动项目会有如下情况:
默认spring security 会保护我们所有的请求,访问任何请求都需要先进行身份认证。进入到一个登陆的页面。
并且在控制台会有打印的密码。
这种情况肯定不符合我们的要求,那我们应该怎么办?
2-覆盖spring securiy的默认配置
对于上面的问题,我们可以覆盖spring securiy的默认配置。
首先我们实现一个Configuration注解的类并且继承WebSecurityConfigurerAdapter:专门做web应用的适配器.
覆盖其方法:
configure(HttpSecurity http)
http.formLogin()
.and()
.authorizeRequests()
.anyRequest()
.authenticated();
上面的配置代表使用表单登录进行身份认证,所有的请求都需要进行身份认证。
3-自定义用户认证逻辑
我们知道了如何覆盖spring securiy的默认配置,接下来就来看看如何自定义用户认证逻辑。
首先我们需要实现UserDetailsService接口,在其loadUserByUsername方法中我们可以自定义用户信息。
比如以下流程:
1-根据用户名去数据库查询用户信息,如密码,是否过期等
2-组装为UserDetails对象然后返回。
spring securiy是一组过滤器,当我们在from表单填入用户名和密码的时候,会进入
UsernamePasswordAuthenticationFilter这个过滤器,它是专门用来处理表单登录请求的过滤器。
这这个过滤器内,它会接收表单传入的用户名和密码,然后进行认证的判断。
我们这里分析一下它是如何进行密码判断的?
首先页面输入了用户名和密码
然后进入了UsernamePasswordAuthenticationFilter过滤器去处理我们的登录请求。
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
在authenticate方法内部它会从UsernamePasswordAuthenticationToken中获取到username,
然后会调用我们自己实现的UserDetailsService来获取用户信息。
UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
然后会根据获取的用户信息进行一系列的判断:
preAuthenticationChecks.check(user);
比如是否过期,是否被冻结,这些信息我们都可以存入库中,查询处理返回给UserDetails 。
然后会对密码进行校验:
if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword()))
这里会获取页面传入的密码和数据库中保存的密码进行校验。
并且使用了passwordEncoder。
passwordEncoder我们可以自定义,在保存用户密码的时候需要使用passwordEncoder对密码加密然后入库。
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
4-个性化用户认证流程
有时候我们需要使用自己的登录页面可以做如下配置:
.loginPage("/free-signIn.html")
.antMatchers("/free-signIn.html").permitAll()
//告诉spring security 在UsernamePasswordAuthenticationFilter去处理我们配置的请求然后去获取用户信息
.loginProcessingUrl("/authentication/form")
.antMatchers(new String[]{"/free-signIn.html","/js/**","/css/**","/img/**",
"/images/**","/fonts/**","/**/favicon.ico"}).permitAll()
可以更加灵活比如:
处理不同类型的请求:
访问需要保护的html
访问需要保护的接口。
配置跳转到自定义的controller方法上:
.loginPage("/authentication/require")
完整代码如下:
//当需要身份认证的时候,跳转到这里
@RequestMapping("/authentication/require")
@ResponseStatus(code = HttpStatus.UNAUTHORIZED)
public SimpleResponse requireAuthentication(HttpServletRequest request, HttpServletResponse response) throws IOException {
SavedRequest savedRequest=requestCache.getRequest(request,response);
if(savedRequest != null){
String targetUrl=savedRequest.getRedirectUrl();
log.info("引发跳转的请求是:"+targetUrl);
if(StringUtils.endsWithIgnoreCase(targetUrl,".html")){
redirectStrategy.sendRedirect(request,response,securityProperties.getBrowser().getLoginPage());
}
}
return new SimpleResponse("访问服务需要身份认证,请引导用户到登录页面");
}
打开App,阅读手记