Spring Boot整合Spring Security

Spring Boot对于该家族的框架支持良好,但是当中本人作为小白配置还是有一点点的小问题,这里分享一下。这个项目是使用之前发布的Spring Boot会员管理系统重新改装,将之前filter登录验证改为Spring Security

  

1. 配置依赖

  Spring Boot框架整合Spring Security只需要添加相应的依赖即可,其后都是配置Spring Security。

  这里使用Maven开发,依赖如下:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

2. 配置Spring Security

2.1 定制WebSecurityConfigurerAdapter

下面通过Java配置Spring Security不拦截静态资源以及需要登录验证等各种信息

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) //开启方法上的认证
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Resource
    private CustomerUserDetailsService userDetailsService;
    @Resource
    private CustomerLoginSuccessHandler successHandler;
    @Resource
    private BCryptPasswordEncoder encoder;

    @Bean
    public BCryptPasswordEncoder encoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/assets/**"); //不过滤静态资源
        super.configure(web);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService) //注册自己定制的UserDetailsService
                .passwordEncoder(encoder); // 配置密码加密器
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http
                .authorizeRequests() //获取请求方面的验证器
                    .antMatchers("/", "/error").permitAll()// 访问当前配置的路径可通过认证
                    //访问其他路径需要认证和角色权限
                    .anyRequest().hasAnyAuthority(AdminRole.G_ADMIN.toString(), AdminRole.S_ADMIN.toString())
                    .anyRequest().authenticated()
                    .and()
                .formLogin() //获取登录认证验证器
                    .loginPage("/login") //注册自定义的登录页面URL
                    .failureForwardUrl("/login") //登录失败后以登录时的请求转发到该链接
                    .successHandler(successHandler) //登录成功后调用该处理器
                    .permitAll() //登录请求给予通过认证
                    .and()
                .logout() //推出登录
                    .logoutSuccessUrl("/login") //退出后访问URL
                    .and()
                .csrf().disable(); //关闭csrf,默认开启

    }
}

  上面的类是用于配置Spring Security框架的,这里有关于几个东西要说明下:

  • @EnableGlobalMethodSecurity

      这是用于配置类 / 方法上的安全认证,它默认关闭了,我们现在配置了@EnableGlobalMethodSecurity(prePostEnabled = true)这将使得可以在方法上使用注解@PreAuthorize("hasAnyAuthority(‘S_ADMIN‘)")(使用范例可看AdminController)在没调用注解下的方法时判断当前认证用户有没有S_ADMIN角色权限操作该方法。

      这个注解可以使得在开发非Web项目时起到作用。

  • configure(AuthenticationManagerBuilder auth)

      这里可以配置该项目与用户的关联,也称作认证。我们需要构建自己的登录验证器,这里我们自定义了一个UserDetailsService和一个密码加密器。

  • configure(HttpSecurity http)

      这里可以配置我们对于一个HttpSecurity需要通过什么样子的安全认证。代码中还包含了csrf().disable(),这是因为框架默认开启了csrf,这样我们的ajax和表单提交等都需要提供一个token,为了偷懒,所以,你懂的

还有就是一个小tip,这是一个配置,相当于xml,Spring只会加载一次,所以以上的方法Spring是初始化一次的

2.2 定制UserDetailsService

  在上面中我们注册了一个自定义的UserDetailsService,这是用于当用户认证时我们需要提供一个可被框架识别的UserDetails,用这个UserDetails和我们的登录用户实体类建立起一个关联,让框架可以处理我们的用户信息,框架所提供的只是usernamepassword,它只是帮助我们认证我们的用户和密码是否匹配。定制UserDetailsService代码如下:

@Component //注册为Spring组件
public class CustomerUserDetailsService implements UserDetailsService{

    @Resource
    private AdminDao adminDao;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //通过dao查找当前用户名对应的用户
        Admin admin = adminDao.findAdminByUsername(username);
        if (admin == null){
            throw new UsernameNotFoundException("This username: "+username+"is not exist");
        }
        //返回一个定制的UserDetails
        //AuthorityUtils.createAuthorityList(admin.getRole())就是将我们该用户所有的权限(角色)生成一个集合
        return new CustomerUserDetails(admin, AuthorityUtils.createAuthorityList(admin.getRole()));

    }
}

  当我们登录时,org.springframework.security.authentication.dao.DaoAuthenticationProvider会调用loadUserByUsername方法并且把当前需要认证的用户名传入,我们需要的就是放回一个通过该用户名得出的拥有密码信息的UserDetails,这样,框架就可以帮助我们验证需要认证的密码与查出的密码是否匹配。

2.3 定制UserDetails

  要让Spring Security能够识别我们定制的UserDetails,那就需要按照它的标准来写,所以,我们需要实现一个UserDetails接口,具体代码如下:

public class CustomerUserDetails implements UserDetails {

    private Admin admin = null;
    //存放权限的集合
    private final Collection<? extends GrantedAuthority> authorities;
    private final boolean accountNonExpired;
    private final boolean accountNonLocked;
    private final boolean credentialsNonExpired;
    private final boolean enabled;

    public CustomerUserDetails(Admin admin, Collection<? extends GrantedAuthority> authorities) {
        this(admin, true, true, true,true,authorities);
    }

    public CustomerUserDetails(Admin admin, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
        if(admin.getUsername() != null && !"".equals(admin.getUsername()) && admin.getPassword() != null) {
            this.admin = admin;
            this.enabled = enabled;
            this.accountNonExpired = accountNonExpired;
            this.credentialsNonExpired = credentialsNonExpired;
            this.accountNonLocked = accountNonLocked;
            this.authorities = authorities;
        } else {
            throw new IllegalArgumentException("Cannot pass null or empty values to constructor");
        }
    }

    public Admin getAdmin() {
        return admin;
    }

    public void setAdmin(@NotNull Admin admin) {
        this.admin = admin;
    }

    public boolean equals(Object rhs) {
        return rhs instanceof CustomerUserDetails && this.getUsername().equals(((CustomerUserDetails) rhs).getUsername());
    }

    public int hashCode() {
        return this.getUsername().hashCode();
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return this.authorities;
    }

    @Override
    public String getPassword() {
        return this.admin.getPassword();
    }

    @Override
    public String getUsername() {
        return this.admin.getUsername();
    }

    @Override
    public boolean isAccountNonExpired() {
        return this.accountNonExpired;
    }

    @Override
    public boolean isAccountNonLocked() {
        return this.accountNonLocked;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return this.credentialsNonExpired;
    }

    @Override
    public boolean isEnabled() {
        return this.enabled;
    }
}

  关于以上的写法是参照Spring Security所提供的User类来改写的,具体代码可看org.springframework.security.core.userdetails.User源码。

  在org.springframework.security.core.userdetails.User中重写hashCodeequals可能是为了判断重复登录的问题,当然了,这只是个人意淫,纯属瞎猜。为了安全起见我也跟着重写了那两个方法了。

  在定制UserDetails我加入了一个成员变量admin,这是因为之前的开发中没有使用该框架,只是使用了filter登录验证,将登录信息存放到 session,所以,为了不大改之前的旧东西,所以我还将会从这里获取admin存于session

2.4 定制AuthenticationSuccessHandler

  定制AuthenticationSuccessHandler不是直接继承接口,而是继承一个实现类SavedRequestAwareAuthenticationSuccessHandler,因为这里保存了我们的登录前请求信息,我们可以不用获取RequestCache就可实现直接从登录页面重定向到登录前访问的URL,具体代码如下:

@Component
public class CustomerLoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        //SecurityContextHolder是Spring Security的核心组件,可获取框架爱内的一些信息
        //这里我得到登录成功后的UserDetails
        Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if (principal instanceof UserDetails) {
            request.getSession().setAttribute("admin", ((CustomerUserDetails) principal).getAdmin());
        }
        super.onAuthenticationSuccess(request, response, authentication);
    }
}

  关于上面代码中的框架核心组件,可以到官方文档中的此链接查看

  定制这个处理器的主要目的是因为我想要偷懒,因为我模板引擎需要访问admin来获取用户名,当然了也可以使用该表达式${session.SPRING_SECURITY_CONTEXT.authentication.principal.username}来获取UserDetails的用户名

3. 总结

  写到这里就差不多该结束了,这里我就做下个人总结。这次整合Spring Security中途不是像这篇文章那样如此下来的,中间磕磕碰碰,所以呢,我就在想,是不是我只是停留在了应用层面才导致的这么个结果,如果了解源码多一点就可能不会出现这些问题了。

以上的代码已上传GitHub

可参考链接

https://docs.spring.io/spring-security/site/docs/5.0.3.RELEASE/reference/htmlsingle/

https://blog.csdn.net/u283056051/article/details/55803855

http://www.tianshouzhi.com/api/tutorials/spring_security_4/250

原文地址:https://www.cnblogs.com/lger/p/8729511.html

时间: 2024-11-05 14:47:03

Spring Boot整合Spring Security的相关文章

Spring Boot 整合Spring Data JPA

Spring Boot整合Spring Data JPA 1)加入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>mysql</groupId> &l

spring boot 整合spring Data JPA+Spring Security+Thymeleaf框架(上)

最近上班太忙所以耽搁了给大家分享实战springboot 框架的使用. 下面是spring boot 整合多个框架的使用. 首先是准备工作要做好. 第一  导入框架所需的包,我们用的事maven 进行对包的管理. 以上的举例是本人的H5DS的真实的后台管理项目,这个项目正在盛情融资中,各位多多捧点人场.关注一下软件发展的动态,说不定以后就是您的生活不可或缺的软件哟. 点击打开链接.闲话少说.现在切入正题. 第二,写点配置文件 第三,spring data -设计一个简单的po关系,这里需要下载一

Spring Boot整合Spring Security总结

一.创建Spring Boot项目 引入Thymeleaf和Web模块以及Spring Security模块方便进行测试,先在pom文件中将 spring-boot-starter-security 的依赖注解掉测试. 二.创建几个用于测试的页面 <!DOCTYPE html><!--index页面--> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head&g

Spring Boot 整合 Spring Security

1.建库 a.创建 用户表.角色表.关系表 CREATE TABLE `sys_user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL, `password` varchar(255) NOT NULL, PRIMARY KEY (`id`) ); CREATE TABLE `sys_role` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar

Spring Boot整合Spring MVC、Spring、Spring Data JPA(Hibernate)

一句话总结:Spring Boot不是新的功能框架,而是为了简化如SSH.SSM等等多个框架的搭建.整合及配置.使用Spring Boot 10分钟搭建起Spring MVC.Spring.Spring Data JPA(Hibernate)基础后台架构.基本零配置,全注解. 步骤一: 使用Spring Boot提供的网站生成maven项目及基础依赖.打开https://start.spring.io/网站,右侧输入想要的特性依赖.输入Web提供整合Spring MVC,输入JPA提供整合Spr

spring boot 整合spring security中spring security版本升级的遇到的坑

在spring security3.x的版本中 hasAnyRole这个方法不会对我们需要认证的身份前面加个前缀ROLE_,在3.x版本hasRole的源码如下 public final boolean hasAnyRole(String... roles) { Set<String> roleSet = getAuthoritySet(); for (String role : roles) { if (roleSet.contains(role)) { return true; } } r

Spring Boot 整合 Spring Cache + Redis

1.安装redis a.由于官方是没有Windows版的,所以我们需要下载微软开发的redis,网址:https://github.com/MicrosoftArchive/redis/releases b.解压后,在redis根目录打开cmd界面,输入:redis-server.exe redis.windows.conf,启动redis(关闭cmd窗口即停止) 2.使用 a.创建SpringBoot工程,选择maven依赖 <dependencies> <dependency>

Spring Kafka和Spring Boot整合实现消息发送与消费简单案例

本文主要分享下Spring Boot和Spring Kafka如何配置整合,实现发送和接收来自Spring Kafka的消息. 先前我已经分享了Kafka的基本介绍与集群环境搭建方法.关于Kafka的介绍请阅读Apache Kafka简介与安装(一),关于Kafka安装请阅读Apache Kafka安装,关于Kafka集群环境搭建请阅读Apache Kafka集群环境搭建 .这里关于服务器环境搭建不在赘述. Spring Kafka整合Spring Boot创建生产者客户端案例 创建一个kafk

Spring Boot --- 认识Spring Boot

在前面我们已经学习过Srping MVC框架,我们需要配置web.xml.spring mvc配置文件,tomcat,是不是感觉配置较为繁琐.那我们今天不妨来试试使用Spring Boot,Spring Boot让我们的Spring应用变的更轻量化.比如:你可以仅仅依靠一个Java类来运行一个Spring引用.你也可以打包你的应用为jar并通过使用java -jar来运行你的Spring Web应用. 一 Spring Boot简介 1.Spring Boot特点 开箱即用,提供各种默认配置来简