JAVA语言之Spring Boot整合Spring Security
小标 2018-07-24 来源 : 阅读 691 评论 0

摘要:本文主要向大家介绍了JAVA语言之Spring Boot整合Spring Security,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助。

本文主要向大家介绍了JAVA语言之Spring Boot整合Spring Security,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助。

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和我们的登录用户实体类建立起一个关联,让框架可以处理我们的用户信息,框架所提供的只是username和password,它只是帮助我们认证我们的用户和密码是否匹配。定制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中重写hashCode和equals可能是为了判断重复登录的问题,当然了,这只是个人意淫,纯属瞎猜。为了安全起见我也跟着重写了那两个方法了。
  在定制UserDetails我加入了一个成员变量admin,这是因为之前的开发中没有使用该框架,只是使用了filter登录验证,将登录信息存放到 session,所以,为了不大改之前的旧东西,所以我还将会从这里获取admin存于session

2.4 定制AuthenticationSuccessHandler

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

@Componentpublic 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

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注编程语言JAVA频道!

本文由 @小标 发布于职坐标。未经许可,禁止转载。
喜欢 | 1 不喜欢 | 0
看完这篇文章有何感觉?已经有1人表态,100%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程