Spring Security 入门(基本使用)

Spring Security 入门(基本使用)
这几天看了下b站关于 spring security 的学习视频,不得不说 spring security 有点复杂,脑袋有点懵懵的,在此整理下学习内容。

1、入门
1.1、什么是 spring security
spring security 是一个比 shiro 更加强大的安全管理框架,权限颗粒度更细。
源自于 spring 家族,能跟 springboot 无缝整合,对 oauth2 的支持也更好。
1.2、依赖配置

org.springframework.boot
spring-boot-starter-security

1.3、测试接口
添加一个简单的 /hello 接口:

@RequestMapping("/hello")
@ResponseBody
public String hello() {
return “恭喜你登录成功”;
}
启动项目,访问 /hello 接口,会发现自动跳转到 spring security 提供的登录页面:

image-20210513103731379

默认的 username 为 :user,password 在项目启动时随机生成,具体如下:

image-20210513104009820

登录成功后即可访问 /hello接口。

2、自定义登录页面、登录成功处理器、登录失败处理器、异常处理器、权限逻辑
项目结构如下:

image-20210514174013950

2.1、自定义登录页面
1、登录页面 login.html :

登陆 用户名:
密码:
立即登陆 2、登录成功跳转页 main.html Title 登录成功!!! 跳转权限页 3、登录失败跳转页 error.html Title 登录失败,请重新登录 跳转 4、权限页 main1.html

**main.html **如果有权限,则能访问该页面,否则报 403

Title 权限控制!!! 2.2、自定义登录逻辑 自定义登录逻辑主要用于对用户名和密码进行校验,需要实现 UserDetailService 接口

@Service
public class UserDetailServiceImpl implements UserDetailsService {

@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    System.out.println("=======执行自定义登录逻辑====");
    //校验用户名,实际环境中需要从数据库查询
    if (!username.equals("admin")) {
        throw new UsernameNotFoundException("用户不存在");
    }
    //比较密码,实际需要从数据库取出原密码校验,框架会自动读取登录页面的密码
    String password = bCryptPasswordEncoder.encode("123456");
    //返回UserDetails,实际开发中可拓展UserDetails
    return new User(username, password, 
                    //自定义权限
                    AuthorityUtils.commaSeparatedStringToAuthorityList("permission1"));
}

}
2.3、自定义登录成功处理器
登录成功处理器实现 AuthenticationSuccessHandler 接口

public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

private String url;

public MyAuthenticationSuccessHandler(String url) {
    this.url = url;
}

@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
    //获取IP地址
    System.out.println(request.getRemoteAddr());
    //获取认证用户信息
    User user = (User) authentication.getPrincipal();
    System.out.println("=====" + user.getAuthorities());
    //重定向
    response.sendRedirect(url);
}

}
2.4、自定义登录失败处理器
登录失败处理器实现 AuthenticationFailureHandler接口

public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {

private String url;

public MyAuthenticationFailureHandler(String url) {
    this.url = url;
}

@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
    //重定向
    response.sendRedirect(url);
}

}
2.5、自定义异常处理器
@Component
public class MyAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
//响应状态403
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
//返回格式
response.setHeader(“Content-Type”, “application/json;charset=utf-8”);
PrintWriter writer = response.getWriter();
writer.write("{status: “error”,“msg”: “权限不足,请联系管理员”}");
writer.flush();
writer.close();
}
}
2.6、配置 Spring Security
该类是 Spring Security 的配置类, 继承 WebSecurityConfigurerAdapter

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired
private MyAccessDeniedHandler myAccessDeniedHandler;

/**
 * 指定密码加密的方法
 *
 * @return
 */
@Bean
public BCryptPasswordEncoder getPasswordEncode() {
    return new BCryptPasswordEncoder();
}

@Override
protected void configure(HttpSecurity http) throws Exception {
    //表单提交
    http.formLogin()
            //自定义用户名和密码参数
            .usernameParameter("username123")
            .passwordParameter("password123")
            //自定义登录页面
            .loginPage("/showLogin")
            //必须和表单提交的接口一样,执行自定义登录逻辑
            .loginProcessingUrl("/login")
            //自定义登录成功处理器
            .successHandler(new MyAuthenticationSuccessHandler("/main.html"))
            //自定义登录失败处理器
            .failureHandler(new MyAuthenticationFailureHandler("/error.html"));
    
    //授权
    http.authorizeRequests()
            //放行/login.html,不需要认证
            .antMatchers("/showLogin").permitAll()
            //放行/error.html,不需要认证
            .antMatchers("/error.html").permitAll()
            //基于权限判断
            .antMatchers("/main1.html").hasAuthority("permission1")
            //所有请求必须认证
            .anyRequest().authenticated();
    
    //异常处理器
    http.exceptionHandling().accessDeniedHandler(myAccessDeniedHandler);

    //关闭csrf防护
    http.csrf().disable();
}

/**
 * 放行静态资源,css,js,images
 * 
 * @param web
 * @throws Exception
 */
@Override
public void configure(WebSecurity web) throws Exception {
    web.ignoring().antMatchers("/css/**", "/js/**")
    .antMatchers("/**/*.png");
}

}
2.7、运行测试
1、运行后访问 http://localhost:8080/login.html,加载的自定义登录页面如下:

注意我在前面的自定义登录逻辑中写死了 username: admin和password:123456

image-20210513145444184

2、点击立即登陆按钮,根据登录成功处理器重定向到登录成功页 main.html:

image-20210513145901250

3、前面的代码中,如果登录成功则拥有permission1权限,而访问权限页刚好需要 permission1 权限,

点击跳转权限页,来到权限页** main1.html**:

image-20210513150430940

4、修改登录成功的权限为 permission2,

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println("===执行自定义登录逻辑");
//校验用户名,实际环境中需要从数据库查询
if (!username.equals(“admin”)) {
throw new UsernameNotFoundException(“用户不存在”);
}
//比较密码,实际需要从数据库取出原密码校验,框架会自动读取登录页面的密码
String password = bCryptPasswordEncoder.encode(“123456”);
//返回UserDetails,实际开发中可拓展UserDetails
return new User(username, password,
//修改权限为permisson2
AuthorityUtils.commaSeparatedStringToAuthorityList(“permission2”));
}
再次访问需要 permission1 权限的权限页,打印以下错误:

image-20210513154239657

5、如果 username 或者 password 错误,根据登录失败处理器重定向到登录失败页 error.html:

image-20210513151019099

3、自定义用户退出登录
3.1、默认的退出登录
spring security 有默认的退出登录接口,直接访问 /logout 接口,就能实现退出登录,下面是简单演示:

main.html 添加退出登录的访问链接logout:

Title 登录成功!!! 退出 跳转权限页 直接就能退出了,简不简单呢?默认跳转到登录页:

image-20210513171702339

仔细观察,发现访问路径拼接了 ?logout 字符串,查看源码可以发现默认的配置如下:

image-20210513172226418

3.2、自定义退出登录
如果默认的退出登录无法满足,可以自定义处理器来解决。

3.2.1、自定义 LogoutHandler
默认情况下清除认证信息 (clearAuthentication),和Session 失效(invalidateHttpSession) 已经由内置的SecurityContextLogoutHandler 来完成。

这个 LogoutHandle 主要用来处理用户信息。

/**

  • 登出接口处理器
    */
    public class MyLogoutHandler implements LogoutHandler {
    @Override
    public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
    User user = (User) authentication.getPrincipal();
    //执行用户信息操作,如记录用户下线时间…
    }
    }
    3.2.2、自定义 LogoutSuccessHandler
    这个 LogoutSuccessHandler 用于返回响应信息给前端,可以返回 json、重定向页面。

注意配置这个处理器之后,就不需要配置 logoutSuccessUrl了。

/**

  • 登出成功处理器
    */
    public class MyLogoutSuccessHandler implements LogoutSuccessHandler {

    private String url;

    public MyLogoutSuccessHandler(String url) {
    this.url = url;
    }
    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
    //重定向
    response.sendRedirect(url);
    }
    }
    3.3.3、spring security 添加配置
    @Override
    protected void configure(HttpSecurity http) throws Exception {
    //表单提交
    http.formLogin()
    //自定义用户名和密码参数
    .usernameParameter(“username123”)
    .passwordParameter(“password123”)
    //自定义登录页面
    .loginPage("/login.html")
    //必须和表单提交的接口一样,执行自定义登录逻辑
    .loginProcessingUrl("/login")
    //自定义登录成功处理器
    .successHandler(new MyAuthenticationSuccessHandler("/main.html"))
    //自定义登录失败处理器
    .failureHandler(new MyAuthenticationFailureHandler("/error.html"));
    //授权
    http.authorizeRequests()
    //放行/login.html,不需要认证
    .antMatchers("/login.html").permitAll()
    //放行/error.html,不需要认证
    .antMatchers("/error.html").permitAll()
    //基于权限判断
    .antMatchers("/main1.html").hasAuthority(“permission1”)
    //所有请求必须认证
    .anyRequest().authenticated();

    //异常处理器
    http.exceptionHandling().accessDeniedHandler(myAccessDeniedHandler);

    //登出
    http.logout()
    //登出接口,与表单访问接口一致
    .logoutUrl("/signLogout")
    //登出处理器
    .addLogoutHandler(new MyLogoutHandler())
    //登出成功后跳转的页面
    .logoutSuccessHandler(new MyLogoutSuccessHandler("/login.html"));

    //关闭csrf防护
    http.csrf().disable();
    }
    3.3.4、修改登出接口
    main.html 修改如下:

Title 登录成功!!! 退出 跳转权限页 运行测试后,返回 localhost://8080/login.html

4、基于注解的权限控制
4.1、权限注解参数
关于权限的注解参数共有三个:

@PreAuthorize:方法执行前进行权限检查
@PostAuthorize:方法执行后进行权限检查
@Secured:类似于 @PreAuthorize
4.2、启动类添加 @EnableGlobalMethodSecurity
启动类配置如下:

@SpringBootApplication
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
public class SpringSecurityStudyApplication {

public static void main(String[] args) {
    SpringApplication.run(SpringSecurityStudyApplication.class, args);
}

}
4.3、运行测试
4.3.1、修改 spring security 和 自定义登录逻辑
successHander(登录成功处理器) 修改为 successForwardUrl(登录成功访问路径),删除 permission1的权限判断,改成访问接口时进行权限判断。

@Override
protected void configure(HttpSecurity http) throws Exception {
//表单提交
http.formLogin()
//自定义用户名和密码参数
.usernameParameter(“username123”)
.passwordParameter(“password123”)
//自定义登录页面
.loginPage("/login.html")
//必须和表单提交的接口一样,执行自定义登录逻辑
.loginProcessingUrl("/login")
//登录成功跳转的页面,post请求
.successForwardUrl("/toMain")
//自定义登录失败处理器
.failureHandler(new MyAuthenticationFailureHandler("/error.html"));
//授权
http.authorizeRequests()
//放行/login.html,不需要认证
.antMatchers("/login.html").permitAll()
//放行/error.html,不需要认证
.antMatchers("/error.html").permitAll()
//所有请求必须认证
.anyRequest().authenticated();

//异常处理器
http.exceptionHandling().accessDeniedHandler(myAccessDeniedHandler);

//登出
http.logout()
    //登出接口,与表单访问接口一致
    .logoutUrl("/signLogout")
    //登出处理器
    .addLogoutHandler(new MyLogoutHandler())
    //登出成功后跳转的页面
    .logoutSuccessHandler(new MyLogoutSuccessHandler("/login.html"));

//关闭csrf防护
http.csrf().disable();

}
自定义登录逻辑如下:

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//校验用户名,实际环境中需要从数据库查询
if (!username.equals(“admin”)) {
throw new UsernameNotFoundException(“用户不存在”);
}
//比较密码,实际需要从数据库取出原密码校验,框架会自动读取登录页面的密码
String password = bCryptPasswordEncoder.encode(“123456”);
//返回UserDetails,实际开发中可拓展UserDetails
return new User(username, password,
//自定义权限
AuthorityUtils.commaSeparatedStringToAuthorityList(“permission1”));
}
4.3.2、添加测试接口
//登录成功跳转页
@PostMapping("/toMain")
//判断是否拥有permission1的权限
@PreAuthorize(“hasPermission(‘permission1’)”)
public String toMain() {
//获得认证用户信息
Object object = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (object instanceof UserDetails) {
//进行一系列操作
}
return “redirect:main.html”;
}
USB Microphone https://www.soft-voice.com/
Wooden Speakers https://www.zeshuiplatform.com/
亚马逊测评 www.yisuping.cn
深圳网站建设www.sz886.com

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值