02-Spring Security 安全框架应用(学一个)

一、网安学习成长路线图

网安所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。
在这里插入图片描述

二、网安视频合集

观看零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。
在这里插入图片描述

三、精品网安学习书籍

当我学到一定基础,有自己的理解能力的时候,会去阅读一些前辈整理的书籍或者手写的笔记资料,这些笔记详细记载了他们对一些技术点的理解,这些理解是比较独到,可以学到不一样的思路。
在这里插入图片描述

四、网络安全源码合集+工具包

光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。
需要体系化学习资料的朋友,可以加我V获取:vip204888 (备注网络安全)

在这里插入图片描述

五、网络安全面试题

最后就是大家最关心的网络安全面试题板块
在这里插入图片描述在这里插入图片描述

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化资料的朋友,可以点击这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

security:

user:

name: jack

#password: 123456 #这种写法,密码太简单了

password: ‘{bcrypt}$2a 10 10 10fahHJIe3SJm3KcyiPPQ2d.a2qR029gB3qKHrKanQ87u.KbtZ6Phr.’

其中,{bcrypt}指定了密码加密时使用的算法

第三步:启动服务,重新进行登录测试.

SpringSecurity 认证逻辑实现

===============================================================================

自定义登陆逻辑


SpringSecurity支持通过配置文件的方式定义用户信息(账号密码和角色等),但这种方式有明显的缺点,那就是系统上线后,用户信息的变更比较麻烦。因此SpringSecurity还支持通过实现UserDetailsService接口的方式来提供用户认证授权信息,其应用过程如下:

第一步:定义security配置类,例如:

/**

  • 由@Configuration注解描述的类为spring中的配置类,配置类会在spring

  • 工程启动时优先加载,在配置类中通常会对第三方资源进行初始配置.

*/

@Configuration

public class SecurityConfig {

/**

  • 定义SpringSecurity密码加密对象

  • @Bean 注解通常会在@Configuration注解描述的类中描述方法,

  • 用于告诉spring框架这个方法的返回值会交给spring管理,并spring

  • 管理的这个对象起个默认的名字,这个名字与方法名相同,当然也可以通过

  • @Bean注解起名字

*/

@Bean //对象名默认为方法名

//@Bean(“bcryptPasswordEncoder”)//bean对象名字为bcryptPasswordEncoder

public BCryptPasswordEncoder passwordEncoder(){

return new BCryptPasswordEncoder();

}

}

第二步:定义UserDetailService接口实现类,自定义登陆逻辑,代码如下:

UserDetailService为SpringSecurity官方提供的登录逻辑处理对象,我们自己可以实现此接口,然后在对应的方法中进行登录逻辑的编写即可.

package com.cy.jt.security.service;

@Service

public class UserDetailServiceImpl implements UserDetailsService {

@Autowired

private BCryptPasswordEncoder passwordEncoder;

/**

  • 当我们执行登录操作时,底层会通过过滤器等对象,调用这个方法.

  • @param username 这个参数为页面输出的用户名

  • @return 一般是从数据库基于用户名查询到的用户信息

  • @throws UsernameNotFoundException

*/

@Override

public UserDetails loadUserByUsername(String username)

throws UsernameNotFoundException {

//1.基于用户名从数据库查询用户信息

//User user=userMapper.selectUserByUsername(username);

if(!“jack”.equals(username))//假设这是从数据库查询的信息

throw new UsernameNotFoundException(“user not exists”);

//2.将用户信息封装到UserDetails对象中并返回

//假设这个密码是从数据库查询出来的

String encodedPwd=passwordEncoder.encode(“123456”);

//假设这个权限信息也是从数据库查询到的

//假如分配权限的方式是角色,编写字符串时用"ROLE_"做前缀

List grantedAuthorities =

AuthorityUtils.commaSeparatedStringToAuthorityList(

“ROLE_admin,ROLE_normal,sys:res:retrieve,sys:res:create”);

//这个user是SpringSecurity提供的UserDetails接口的实现,用于封装用户信息

//后续我们也可以基于需要自己构建UserDetails接口的实现

User user=new User(username,encodedPwd,grantedAuthorities);

return user;

}

}

说明,这里的User对象会交给SpringSecurity框架,框架提取出密码信息,然后与用户输入的密码进行匹配校验.

第三步:启动服务进行登陆,访问测试。

在这里插入图片描述

在这里插入图片描述

自定义登陆页面


第一步:定义登陆页面(直接在static目录下创建即可),关键代码如下:

Please sign in

Sign in

注意:请求的url暂时为”/login”,请求方式必须为post方式,请求的参数暂时必须为username,password。这些规则默认在UsernamePasswordAuthenticationFilter中进行了定义。

第二步:修改安全配置类,让其实现接口,并重写相关config方法,进行登陆设计,代码如下:

@Configuration

public class SecutiryConfig extends WebSecurityConfigurerAdapter {

@Bean

public PasswordEncoder passwordEncoder(){

return new BCryptPasswordEncoder();

}

@Override

protected void configure(HttpSecurity http) throws Exception {

//super.configure(http);

//关闭跨域攻击,不关闭容易出错

http.csrf().disable();

//自定义登陆表单

http.formLogin()

//设置登陆页面

.loginPage(“/login.html”)

//设置登陆请求处理地址(对应form表单中的action),登陆时会访问UserDetailService对象

.loginProcessingUrl(“/login”)

//设置请求用户名参数为username(默认就是username,可以自己修改,需要与表单同步)

.usernameParameter(“username”)

//请求请求密码参数为password(默认就是password,可以自己修改,需要与表单同步)

.passwordParameter(“password”)

//设置登陆成功跳转页面(默认为/index.html)

.defaultSuccessUrl(“/index.html”)

//登陆失败访问的页面(默认为/login.html?error)

.failureUrl(“/login.html?error”);

//认证设计

http.authorizeRequests()

//设置要放行的咨询

.antMatchers(“/login.html”).permitAll()

//设置需要认证的请求(除了上面的要放行,其它都要进行认证)

.anyRequest().authenticated();

}

}

登陆成功和失败处理器


现在的很多系统都采用的是前后端分离设计,我们登陆成功以后可能会跳转到前端系统的某个地址,或者返回一个json数据,我们可以自己定义登录成功的处理操作,例如:

定义登陆成功处理器:

方案1:可以直接执行重定向的处理器,例如

package com.cy.jt.auth.config.authentication;

public class RedirectAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

//定义要跳转的url

private String redirectUrl;

public RedirectAuthenticationSuccessHandler(String redirectUrl){

this.redirectUrl=redirectUrl;

}

@Override

public void onAuthenticationSuccess(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse,

Authentication authentication)

throws IOException, ServletException {

httpServletResponse.sendRedirect(redirectUrl);

}

}

方案2:可以直接返回JSON数据的处理器,例如:

package com.cy.jt.security.config.handler;

/**处理登录失败

  • 0)Default-默认

  • 1)Authentication-认证

  • 2)Failure-失败

  • 3)Handler-处理器

  • */

public class DefaultAuthenticationFailureHandler implements AuthenticationFailureHandler {

@Override

public void onAuthenticationFailure(

HttpServletRequest httpServletRequest,

HttpServletResponse httpServletResponse,

AuthenticationException e) throws IOException, ServletException {

//1.设置响应数据的编码

httpServletResponse.setCharacterEncoding(“utf-8”);

//2.告诉客户端响应数据的类型,以及客户端以怎样的编码进行显示

httpServletResponse.setContentType(“application/json;charset=utf-8”);

//3.获取一个输出流对象

PrintWriter out=httpServletResponse.getWriter();

//4.向客户端输出一个json格式字符串

//4.1构建一个map对象

Map<String,Object> map=new HashMap<>();

map.put(“state”,“500”);

map.put(“msg”,“username or password error”);

//4.2基于jackson中的ObjectMapper对象将一个对象转换为json格式字符串

String jsonStr= new ObjectMapper().writeValueAsString(map);

out.println(jsonStr);

out.flush();

}

}

定义登陆失败处理器:

方案1:登陆失败重定向到页面,例如

package com.cy.jt.auth.config.authentication;

public class RedirectAuthenticationFailureSuccessHandler implements AuthenticationFailureHandler {

private String redirectUrl;

public RedirectAuthenticationFailureSuccessHandler(String redirectUrl){

this.redirectUrl=redirectUrl;

}

@Override

public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {

httpServletResponse.sendRedirect(redirectUrl);

}

}

方案2:定义登陆失败处理器,例如:

package com.cy.jt.security.config.handler;

/**处理登录失败

  • 0)Default-默认

  • 1)Authentication-认证

  • 2)Failure-失败

  • 3)Handler-处理器

  • */

public class DefaultAuthenticationFailureHandler

implements AuthenticationFailureHandler {

@Override

public void onAuthenticationFailure(

HttpServletRequest httpServletRequest,

HttpServletResponse httpServletResponse,

AuthenticationException e) throws IOException, ServletException {

//1.设置响应数据的编码

httpServletResponse.setCharacterEncoding(“utf-8”);

//2.告诉客户端响应数据的类型,以及客户端以怎样的编码进行显示

httpServletResponse.setContentType(“application/json;charset=utf-8”);

//3.获取一个输出流对象

PrintWriter out=httpServletResponse.getWriter();

//4.向客户端输出一个json格式字符串

//4.1构建一个map对象

Map<String,Object> map=new HashMap<>();

map.put(“state”,“500”);

map.put(“msg”,“username or password error”);

//4.2基于jackson中的ObjectMapper对象将一个对象转换为json格式字符串

String jsonStr= new ObjectMapper().writeValueAsString(map);

out.println(jsonStr);

out.flush();

}

}

修改配置类,设置登陆成功与失败处理器。

@Configuration

public class SecutiryConfig extends WebSecurityConfigurerAdapter {

@Bean

public PasswordEncoder passwordEncoder(){

return new BCryptPasswordEncoder();

}

@Override

protected void configure(HttpSecurity http) throws Exception {

//super.configure(http);

//关闭跨域攻击,不关闭容易出错

http.csrf().disable();

//自定义登陆表单

http.formLogin()

//设置登陆页面

.loginPage(“/login.html”)

//设置登陆请求处理地址(对应form表单中的action),登陆时会访问UserDetailService对象

.loginProcessingUrl(“/login”)

//设置请求用户名参数为username(默认就是username,可以自己修改,需要与表单同步)

.usernameParameter(“username”)

//请求请求密码参数为password(默认就是password,可以自己修改,需要与表单同步)

.passwordParameter(“password”)

//设置登陆成功跳转页面(默认为/index.html)

.successHandler(new RedirectAuthenticationSuccessHandler(“你的url”))

//登陆失败访问的页面(默认为/login.html?error)

.failureHandler(new RedirectAuthenticationFailureHandler(“你的url”))

//认证设计

http.authorizeRequests()

//设置要放行的咨询

.antMatchers(“/login.html”).permitAll()

//设置需要认证的请求(除了上面的要放行,其它都要进行认证)

.anyRequest().authenticated();

}

}

第四步:启动服务进行访问测试(分别用正确和错误的账号进行测试)。

放行静态资源


在SecurityManager配置类中的configure(HttpSecurity http)方法中我们可以通过对anMatchers方法定义要放行静态资源,例如:

.authorizeRequests() //设置请求的授权

.antMatchers( //配置下列路径的授权

“/index.html”,

“/js/*”,

“/css/*”,

“/img/**”,

“/bower_components/**”,

“/login.html”

).permitAll() //设置上述所有路径不需要登录就能访问(放行)

其中:

  • “*”用于匹配0个或多个字符

  • “**”用于匹配0个或多个目录及字符

登出设计及实现


在SecurityManager配置类中的configure(HttpSecurity http)方法中,添加登出配置,例如

http.logout() //开始设置登出信息

.logoutUrl(“/logout”) //登出路径

.logoutSuccessUrl(“/login.html?logout”);//设置登出后显示的页面

SpringSecurity授权逻辑实现

==============================================================================

修改授权配置类


在权限配置类上添加启用全局方法访问控制注解,例如:

package com.cy.auth.config;

//这个配置类是配置Spring-Security的,

//prePostEnabled= true表示启动权限管理功能

@EnableGlobalMethodSecurity(prePostEnabled = true)

@Configuration

public class SpringSecurityConfigurer extends WebSecurityConfigurerAdapter {

……

}

定义资源Controller


定义一个ResourceController类,作为资源访问对象,例如

package com.cy.jt.auth.controller;

@RestController

public class ResourceController {

@PreAuthorize(“hasAuthority(‘sys:res:create’)”)

@RequestMapping(“/doCreate”)

public String doCreate(){

return “add resource”;

}

@PreAuthorize(“hasAuthority(‘sys:res:update’)”)

@RequestMapping(“doUpdate”)

public String doUpdate(){

return “update resource”;

}

@PreAuthorize(“hasAuthority(‘sys:res:delete’)”)

@RequestMapping(“/doDelete”)

public String doDelete(){

return “delete resource”;

}

@PreAuthorize(“hasAuthority(‘sys:res:retrieve’)”)

@RequestMapping(“/doRetrieve”)

public String doRetrieve(){

return “retrieve resource”;

}

}

其中,@PreAuthorize注解描述方法时,用于告诉系统访问此方法时需要进行权限检测。需要具备指定权限才可以访问。例如:

  • @PreAuthorize(“hasAuthority('sys:res:delete”) 需要具备sys:res:delete权限

  • @PreAuthorize(“hasRole(‘admin’)”) 需要具备admin角色

启动服务访问测试


使用不同用户进行登陆,然后执行资源访问,假如没有权限,则会看到响应状态吗403,如图所示:

在这里插入图片描述

Spring认证和授权异常处理

=========================================================================

异常类型


对于SpringSecurity框架而言,在实现认证和授权业务时,可能出现如下两大类型异常:

1)AuthenticationException (用户还没有认证就去访问某个需要认证才可访问的方法时,可能出现的异常,这个异常通常对应的状态码401)

2)AccessDeniedException (用户认证以后,在访问一些没有权限的资源时,可能会出现的异常,这个异常通常对应的状态吗为403)

异常处理规范


SpringSecurity框架给了默认的异常处理方式,当默认的异常处理方式不满足我们实际业务需求时,此时我们就要自己定义异常处理逻辑,编写逻辑时需要遵循如下规范:

1)AuthenticationEntryPoint:统一处理 AuthenticationException 异常

2)AccessDeniedHandler:统一处理 AccessDeniedException 异常.

自定义异常处理对象


处理没有认证的访问异常

package com.cy.jt.config;

public class DefaultAuthenticationEntryPoint implements AuthenticationEntryPoint {

@Override

public void commence(HttpServletRequest request,

HttpServletResponse response,

AuthenticationException e) throws IOException, ServletException {

//设置响应数据的编码

response.setCharacterEncoding(“utf-8”);

//告诉浏览器要响应的内容类型,以及编码

response.setContentType(“application/json;charset=utf-8”);

Map<String,Object> map=new HashMap<>();

map.put(“state”,401);

map.put(“message”,“请先登录”);

PrintWriter out=response.getWriter();

out.println(new ObjectMapper().writeValueAsString(map));

out.flush();

out.close();

}

}

处理没有权限时抛出的异常

package com.cy.jt.config;

public class DefaultAccessDeniedExceptionHandler implements AccessDeniedHandler {

@Override

public void handle(HttpServletRequest request,

HttpServletResponse response,

AccessDeniedException e) throws IOException, ServletException {

//设置响应数据的编码

response.setCharacterEncoding(“utf-8”);

//告诉浏览器要响应的内容类型,以及编码

response.setContentType(“application/json;charset=utf-8”);

Map<String,Object> map=new HashMap<>();

map.put(“state”,403);

map.put(“message”,“没有此资源的访问权限”);

PrintWriter out=response.getWriter();

out.println(new ObjectMapper().writeValueAsString(map));

out.flush();

out.close();

}

}

配置异常处理对象


在配置类SecurityConfig中添加自定义异常处理对象,代码如下

http.exceptionHandling()

.authenticationEntryPoint(new DefaultAuthenticationEntryPoint())

.accessDeniedHandler(new DefaultAccessDeniedExceptionHandler());

配置完成后,重启服务进行访问测试分析.

系统会话状态分析与实践

=====================================================================

何为会话状态


客户端与服务端通讯过程中产生的状态信息(类似会议记录),称之为会话状态.

会话状态如何存储


客户端浏览器与服务端通讯时使用的是http协议,这个协议本身是无状态协议,也就是说通过此协议,无法存储会话状态,此时在服务端与客户端就采用了一种Cookie与Session方式记录会话状态.

有状态的会话技术分析


  • Cookie 技术

Cookie是由服务端创建但在客户端存储会话状态的一个对象,此对象分为两种类型,一种为会话Cookie,一种为持久Cookie,浏览器在访问具体的某个域名时会携带这个域的有效Cookie到服务端.

  1. 会话Cookie: 浏览器关闭Cookie生命周期结束(一般默认都是会话Cookie)

  2. 持久Cookie: 持久Cookie是在Cookie对象创建时指定了生命周期,例如一周时间,即便浏览器关闭,持久Cookie依旧有效.

  • Session技术

Session技术由服务端创建,并在服务端存储会话状态的一个对象,当Session对象创建时,还会创建一个会话Cookie对象,并且通过这个会话Cookie将SessionId写到客户端,客户端下次访问服务端会携带这个会话Cookie,并且通过JsessionId找到Session对象,进而获取Session对象中存储的数据.Cookie默认的生命周期为30分钟.

在SpringSecurity中获取用户的认证信息,就可以通过如下方式进行实现:

Authentication authentication =

SecurityContextHolder.getContext().getAuthentication();

无状态的会话技术分析


有状态的会话实现,在分布式架构中可能会存在很多问题,例如浏览器默认不支持携带其它域的Cookie信息进行资源访问,同时服务端的Session默认不能共享,当然我们有一种方式可以将session持久化到到一些数据库,例如Redis,下次请求到其它服务器(例如tomcat)时,可以直接从redis中获取登录信息,但是假如并发比较大,数据库的访问压力就会剧增,压力太大有可能会导致系统宕机.所以现在还有一种方案就是将用户的登录状态信息都存储在客户端,服务端不记录任何状态,服务端只负责对客户端传递过来的状态信息进行解析,基于此方式进行用户登录状态的判断,这样的会话过程称之为无状态会话.

总结(Summary)

给大家的福利

零基础入门

对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。

同时每个成长路线对应的板块都有配套的视频提供:

在这里插入图片描述

因篇幅有限,仅展示部分资料

需要体系化学习资料的朋友,可以加我V获取:vip204888 (备注网络安全)

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化资料的朋友,可以点击这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 15
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值