Spring Boot整合Spring Security的示例代码

本文讲述Spring Boot整合Spring Security在方法上使用注解实现权限控制,使用自定义UserDetailService,从MySQL中加载用户信息。使用Security自带的MD5加密,对用户密码进行加密。页面模板采用thymeleaf引擎。

源码地址:https://github.com/li5454yong/springboot-security.git

1、引入pom依赖

 <parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>1.4.4.RELEASE</version>
 </parent>

 <dependencies>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-security</artifactId>
  </dependency>
  <dependency>
   <groupId>org.springframework.security.oauth</groupId>
   <artifactId>spring-security-oauth2</artifactId>
  </dependency>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-thymeleaf</artifactId>
  </dependency>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-jpa</artifactId>
  </dependency>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-jdbc</artifactId>
  </dependency>
  <dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
   <version>5.1.34</version>
  </dependency>
  <dependency>
   <groupId>com.alibaba</groupId>
   <artifactId>druid</artifactId>
   <version>1.0.15</version>
  </dependency>
 </dependencies>

这里使用druid连接池,Spring Data Jpa实现数据库访问。

2、配置Spring Security

@Configuration
@EnableWebMvcSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)//开启security注解
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
 @Bean
 @Override
 protected AuthenticationManager authenticationManager() throws Exception {
  return super.authenticationManager();
 }

 @Override
 protected void configure(HttpSecurity http) throws Exception {
  //允许所有用户访问"/"和"/home"
  http.authorizeRequests()
    .antMatchers("/", "/home").permitAll()
    //其他地址的访问均需验证权限
    .anyRequest().authenticated()
    .and()
    .formLogin()
    //指定登录页是"/login"
    .loginPage("/login")
    .defaultSuccessUrl("/hello")//登录成功后默认跳转到"/hello"
    .permitAll()
    .and()
    .logout()
    .logoutSuccessUrl("/home")//退出登录后的默认url是"/home"
    .permitAll();
 }

 @Autowired
 public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
  auth
   .userDetailsService(customUserDetailsService())
   .passwordEncoder(passwordEncoder());

 }

 /**
  * 设置用户密码的加密方式为MD5加密
  * @return
  */
 @Bean
 public Md5PasswordEncoder passwordEncoder() {
  return new Md5PasswordEncoder();
 }

 /**
  * 自定义UserDetailsService,从数据库中读取用户信息
  * @return
  */
 @Bean
 public CustomUserDetailsService customUserDetailsService(){
  return new CustomUserDetailsService();
 }
}

这里只做了基本的配置,设置了登录url、登录成功后跳转的url、退出后跳转到的url。使用@EnableGlobalMethodSecurity(prePostEnabled = true)这个注解,可以开启security的注解,我们可以在需要控制权限的方法上面使用@PreAuthorize,@PreFilter这些注解。

3、自定义userDetailService

public class CustomUserDetailsService implements UserDetailsService {
 @Autowired //数据库服务类
 private SUserService suserService;

 @Override
 public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
  //SUser对应数据库中的用户表,是最终存储用户和密码的表,可自定义
  //本例使用SUser中的email作为用户名:
  SUser user = suserService.findUserByEmail(userName); 

  if (user == null) {

   throw new UsernameNotFoundException("UserName " + userName + " not found");

  }

  // SecurityUser实现UserDetails并将SUser的Email映射为username
  SecurityUser securityUser = new SecurityUser(user);
  Collection<SimpleGrantedAuthority> authorities = new ArrayList<SimpleGrantedAuthority>();
  authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
  return securityUser; 

 }

}

这里只需要实现UserDetailsService 接口,重写loadUserByUsername方法,从数据库中取出用户信息。最后返回一个UserDetails 实现类。

4、定义错误处理配置

@Configuration
public class ErrorPageConfig {
 @Bean
 public EmbeddedServletContainerCustomizer embeddedServletContainerCustomizer(){
  return new MyCustomizer();
 }
 private static class MyCustomizer implements EmbeddedServletContainerCustomizer {
  @Override
  public void customize(ConfigurableEmbeddedServletContainer container) {
   container.addErrorPages(new ErrorPage(HttpStatus.FORBIDDEN, "/403"));
  }
 }
}

访问发生错误时,跳转到”/403”.

5、Controller接口

@Controller
public class IndexController {

 @Resource
 private SUserService sUserService;

 @RequestMapping("/home")
 public String home() {
  return "home";

 }

 @PreAuthorize("hasRole('user')")
 @RequestMapping(value = "/admin",method = RequestMethod.GET)
 public String toAdmin(){

  return "helloAdmin";
 }

 @RequestMapping("/hello")
 public String hello() {

  return "hello";

 }

 @RequestMapping("/login")
 public String login(){
  return "login";
 }

 @RequestMapping("/")
 public String root() {
  return "index";

 }

 @RequestMapping("/403")
 public String error(){
  return "403";
 }
}

在toAdmin()方法上面使用了@PreAuthorize(“hasRole(‘user')”),表示访问这个方法需要拥有user角色。如果希望控制到权限层面,可以使用@PreAuthorize(“hasPermission()”)。这里只是用了其中的一个用法,更多的使用方法可以去看官方文档。需要注意的是,Spring Security默认的角色前缀是”ROLE_”,使用hasRole方法时已经默认加上了,因此我们在数据库里面的用户角色应该是“ROLE_user”,在user前面加上”ROLE_”前缀。

6、测试

启动项目,访问http://localhost:1130/login

点击登录后进入到“/hello”

点击跳转到管理员页面

在后台的“/admin”这个url对应的方法上面,限制了用户必须要拥有“user”角色。在数据库中也设置了登录的用户有这个角色。
现在我们修改数据库中的用户角色,改为“ROLE_admin”。退出登录后重新登录,再次点击“前往管理员页面”按钮,会跳转到如下页面。

因为现在没有了“user”权限,所以访问的时候抛出了异常,被拦截后重定向到了“/403”。

7、POST方式访问,错误码403

首先,把“/admin”改为POST请求

 @PreAuthorize("hasRole('user')")
 @RequestMapping(value = "/admin",method = RequestMethod.POST)
 public String toAdmin(){
  return "helloAdmin";
 }

把“前往管理员页面”按钮的请求方式从原来的form表达get提交,改为ajax方式POST提交。至于为什么不是用form的POST提交后面再讲。先修改代码

<body>

<h1 th:inline="text">Hello [[${#httpServletRequest.remoteUser}]]!</h1>

<!--<form th:action="@{/logout}" method="post">

 <input type="submit" value="Sign Out"/>
</form>
<form th:action="@{/admin}" method="get">
 <input th:type="submit" th:value="前往管理员页面"/>

</form>-->
<a th:href="@{/admin}" rel="external nofollow" >前往管理员用户页面</a>
<input th:type="submit" onclick="testPost()" th:value="前往管理员页面"/>
</body>
<script>
 function testPost() {
  $.ajax({
   url:"/admin",
   type:'POST',

   success:function (data) {

   }
  });
 }
</script>

点击“前往管理员页面”按钮,在调试台可以看到如下

这是因为框架内部防止CSRF(Cross-site request forgery跨站请求伪造)的发生,限制了除了get以外的大多数方法。

下面说解决办法:

首先在标签内添加如下内容。

 <meta name="_csrf" th:content="${_csrf.token}"/>
 <meta name="_csrf_hader" th:content="${_csrf.headerName}"/>

只要添加这个token,后台就会验证这个token的正确性,如果正确,则接受post访问。
然后在ajax代码中添加以下代码:

var token = $('meta[name="_csrf"]').attr("content");
var header = $('meta[name="_csrf_hader"]').attr("content");
$(document).ajaxSend(function(e,xhr,opt){
   xhr.setRequestHeader(header,token);
  });

这样就可以正常使用POST、DELETE等其他方式来访问了。

上面说到使用表单的POST方式来提交,通过查看页面源代码可以看到

框架在form表单中自动插入了一个隐藏域,value值就是那个token,所以使用form表单来提交POST请求是可以直接通过的,而ajax方式提交的话,需要加上那段代码。

好了,这篇文章就讲到这,后面还会有文章讲述REST API风格如何来使用Spring Security来控制权限。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 深入浅析 Spring Security 缓存请求问题

    为什么要缓存? 为了更好的描述问题,我们拿使用表单认证的网站举例,简化后的认证过程分为7步: 用户访问网站,打开了一个链接(origin url). 请求发送给服务器,服务器判断用户请求了受保护的资源. 由于用户没有登录,服务器重定向到登录页面 填写表单,点击登录 浏览器将用户名密码以表单形式发送给服务器 服务器验证用户名密码.成功,进入到下一步.否则要求用户重新认证(第三步) 服务器对用户拥有的权限(角色)判定: 有权限,重定向到origin url; 权限不足,返回状态码403("forbi

  • Ajax登陆使用Spring Security缓存跳转到登陆前的链接

    Spring Security缓存的应用之登陆后跳转到登录前源地址 什么意思? 用户访问网站,打开了一个链接:(origin url)起源链接 请求发送给服务器,服务器判断用户请求了受保护的资源. 由于用户没有登录,服务器重定向到登录页面:/login 填写表单,点击登录 浏览器将用户名密码以表单形式发送给服务器 服务器验证用户名密码.成功,进入到下一步.否则要求用户重新认证(第三步) 服务器对用户拥有的权限(角色)判定.有权限,重定向到origin url; 权限不足,返回状态码403( "禁

  • 详解利用spring-security解决CSRF问题

    CSRF介绍 CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF. 具体SCRF的介绍和攻击方式请参看百度百科的介绍和一位大牛的分析: CSRF百度百科 浅谈CSRF攻击方式 配置步骤 1.依赖jar包 <properties> <spring.security.version>4.2.2.RELEASE</spring.security

  • Spring Boot整合Spring Security简单实现登入登出从零搭建教程

    前言 Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架.它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作. 本文主要给大家介绍了关于Spring Boot整合S

  • SpringBoot + Spring Security 基本使用及个性化登录配置详解

    Spring Security 基本介绍 这里就不对Spring Security进行过多的介绍了,具体的可以参考官方文档 我就只说下SpringSecurity核心功能: 认证(你是谁) 授权(你能干什么) 攻击防护(防止伪造身份) 基本环境搭建 这里我们以SpringBoot作为项目的基本框架,我这里使用的是maven的方式来进行的包管理,所以这里先给出集成Spring Security的方式 <dependencies> ... <dependency> <groupI

  • Spring Security OAuth2集成短信验证码登录以及第三方登录

    前言 基于SpringCloud做微服务架构分布式系统时,OAuth2.0作为认证的业内标准,Spring Security OAuth2也提供了全套的解决方案来支持在Spring Cloud/Spring Boot环境下使用OAuth2.0,提供了开箱即用的组件.但是在开发过程中我们会发现由于Spring Security OAuth2的组件特别全面,这样就导致了扩展很不方便或者说是不太容易直指定扩展的方案,例如: 图片验证码登录 短信验证码登录 微信小程序登录 第三方系统登录 CAS单点登录

  • SpringBoot结合SpringSecurity实现图形验证码功能

    本文介绍了SpringBoot结合SpringSecurity实现图形验证码功能,分享给大家,具体如下: 生成图形验证码 根据随机数生成图片 将随机数存到Session中 将生成的图片写到接口的响应中 生成图形验证码的过程比较简单,和SpringSecurity也没有什么关系.所以就直接贴出代码了 根据随机数生成图片 /** * 生成图形验证码 * @param request * @return */ private ImageCode generate(ServletWebRequest r

  • 详解如何在spring boot中使用spring security防止CSRF攻击

    CSRF是什么? CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF.  CSRF可以做什么? 你这可以这么理解CSRF攻击:攻击者盗用了你的身份,以你的名义发送恶意请求.CSRF能够做的事情包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账......造成的问题包括:个人隐私泄露以及财产安全. CSRF漏洞现状 CSRF这种攻击方式

  • SpringBoot+SpringSecurity处理Ajax登录请求问题(推荐)

    最近在项目中遇到了这样一个问题:前后端分离,前端用Vue来做,所有的数据请求都使用vue-resource,没有使用表单,因此数据交互都是使用JSON,后台使用Spring Boot,权限验证使用了Spring Security,因为之前用Spring Security都是处理页面的,这次单纯处理Ajax请求,因此记录下遇到的一些问题.这里的解决方案不仅适用于Ajax请求,也可以解决移动端请求验证. 创建工程 首先我们需要创建一个Spring Boot工程,创建时需要引入Web.Spring S

  • SpringBoot与spring security的结合的示例

    权限控制,也是我们再日常开发中经常遇到的场景,需要根据用户的角色决定是否可以看到某个资源.目前,市面上此类框架主要有shiro与我们今天要讲到的spring security.关于权限的控制有复杂的控制,例如几乎每个公司都有单点登录系统,根据用户名来到数据库中拿到对应的权限,在展示该权限下能看到的资源.还有一种就是简单的控制,也就是我们今天所要提到的.将账号,密码,角色配置到代码中,也可以进行简单的控制,缺点不言而喻,扩展性不好,只有固定的账号,但是作为演示还是够用的. 好了废话不多说,上pom

  • Spring Security使用中Preflight请求和跨域问题详解

    Spring Security Spring Security是能够为J2EE项目提供综合性的安全访问控制解决方案的安全框架.它依赖于Servlet过滤器.这些过滤器拦截进入请求,并且在应用程序处理该请求之前进行某些安全处理. Spring Security对用户请求的拦截过程如下: 背景 在一个前后端分离开发的项目中,使用SpringSecurity做安全框架,用JWT来实现权限管理提升RESTful Api的安全性.首先遇到的就是跨域问题,但是在携带jwt请求过程中出现了服务端获取不到jwt

  • 详解spring security之httpSecurity使用示例

    httpSecurity 类似于spring security的xml配置文件命名空间配置中的<http>元素.它允许对特定的http请求基于安全考虑进行配置.默认情况下,适用于所有的请求,但可以使用requestMatcher(RequestMatcher)或者其它相似的方法进行限制. 使用示例: 最基本的基于表单的配置如下.该配置将所有的url访问权限设定为角色名称为"ROLE_USER".同时也定义了内存认证模式:使用用户名"user"和密码&qu

随机推荐