前言

(一). 什么是Spring Security? 

(二). 相关配置

(三). Spring Security实战

本篇文章接着上篇内容继续为大家介绍SpringBoot中 实现权限控制。

springboot个人博客权限管理模块的开发 spring boot权限控制_spring

 

(一). 什么是Spring Security? 

在web应用开发中,安全无疑是十分重要的,选择Spring Security来保护web应用是一个非常好的选择。Spring Security 是spring项目之中的一个安全模块,可以非常方便与spring项目无缝集成。特别是在spring boot项目中加入spring security更是十分简单。本篇我们介绍spring security,以及spring security在web应用中的使用。

Spring Security,这是一种基于 Spring AOP 和 Servlet 过滤器的安全框架。它提供全面的安全性解决方案,同时在 Web 请求级和方法调用级处理身份确认和授权。

(二). 相关配置

http.authorizeRequests()     .anyRequest().authenticated()     .and()     .formLogin()     .and()
          //开启cookie保 用户数据
          .rememberMe()
          //设置cookie有效期
          .tokenValiditySeconds(60 * 60 * 24 * 7)
          //设置cookie的私钥
          .key("")
          .logout()
          //默认注销行为为logout,可以通过下面的方式来修改
           .logoutUrl("/custom-logout")
          //设置注销成功后跳转页面,默认是跳转到登录页面
          .logoutSuccessUrl("")
     .loginPage("/login")     .permitAll()
    .antMatchers("/resources/**", "/signup", "/about")
    .antMatchers("/admin/**")
    .hasRole("ADMIN")     .antMatchers("/db/**")
    .access("hasRole('ADMIN') and hasRole('DBA')")     .anyRequest()
    .authenticated()     .httpBasic();

http.authorizeRequests()方法有多个子节点,每个匹配器按其声明

的顺序进行考虑。上面列出了常用的基本配置,包括请求授权,登录页面路径,

成功后跳转等

  • 1.http.authorizeRequests()方法有多个子节点,每个匹配器按其声明的顺序进行考虑。
  • 2我们指定了任何用户都可以访问的多种URL模式。具体来说,如果URL以“/ resources /”开头,等于“/ signup”或等于“/ about”,则任何用户都可以访问请求。
  • 3 任何以“/ admin /”开头的URL都将仅限于具有“ROLE_ADMIN”角色的用户。您会注意到,由于我们正在调用hasRole方法,因此我们不需要指定“ROLE_”前缀。
  • 4 任何以“/ db /”开头的URL都要求用户同时拥有“ROLE_ADMIN”和“ROLE_DBA”。您会注意到,由于我们使用的是hasRole表达式,因此我们不需要指定“ROLE_”前缀。
  • 5 任何尚未匹配的URL只需要对用户进行身份验证

(三). Spring Security实战

#创建项目

springboot个人博客权限管理模块的开发 spring boot权限控制_java_02

springboot个人博客权限管理模块的开发 spring boot权限控制_ide_03

springboot个人博客权限管理模块的开发 spring boot权限控制_ide_04

#数据库配置

我们这里还是通过mysql数据库来实现,配置数据库依赖
 

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.40</version>
</dependency>

配置数据库 application.properties

spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/springbootsecurity?useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=root
.springframework.security=info
spring.thymeleaf.cache=false
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jackson.serialization.indent_output=true

#用户和角色

我们使用JPA来定义用户和角色

package org.cxzc.myyoung.springbootsecurity;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@Entity
public class SysUser implements UserDetails {
    @Id
    @GeneratedValue
    private Long id;
    private String username;
    private String password;
    @ManyToMany(cascade = {CascadeType.REFRESH},fetch = FetchType.EAGER)
    private List<SysRole> roles;
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public List<SysRole> getRoles() {
        return roles;
    }
    public void setRoles(List<SysRole> roles) {
        this.roles = roles;
    }
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<GrantedAuthority> auths = new ArrayList<>();
        List<SysRole> roles = this.getRoles();
        for (SysRole role : roles) {
            auths.add(new SimpleGrantedAuthority(role.getName()));
        }
        return auths;
    }
    @Override
    public String getPassword() {
        return this.password;
    }
    @Override
    public String getUsername() {
        return this.username;
    }
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }
    @Override
    public boolean isEnabled() {
        return true;
    }
}

1,用户实体实现UserDetails接口,用户实体为spring security所使用的用户

2,配置用户和角色的多对多关系

3,重写 getAuthorities 方法,将用户的角色作为权限

#角色类

package org.cxzc.myyoung.springbootsecurity;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import ;
@Entity
public class SysRole {
    @Id
    @GeneratedValue
    private Long id;
    private String name;
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
         = name;
    }
}

1,ID为自动生成

2,name为角色名称

#数据结构及初始化

上面两个实体类创建的目的就是为了 生成需要的数据表,这时会生成三张表
 

springboot个人博客权限管理模块的开发 spring boot权限控制_ide_05

 

用户表:SYS_USER
角色表:SYS_ROLE

关联表:SYS_USER_ROLES

针对上面的三张表,创建一些数据来验证,数据创建格式如下:

insert  into `sys_role`(`id`,`name`) values (1,'ROLE_ADMIN'),(2,'ROLE_USER');
insert  into `sys_user`(`id`,`password`,`username`) values (1,'root','root'),(2,'chen','chen');
insert  into `sys_user_roles`(`sys_user_id`,`roles_id`) values (1,1),(2,2);

创建两个用户,角色分别为 ROLE_ADMIN 和ROLE_USER
#传值对象
用来测试不同角色用户的数据展示。

package org.cxzc.myyoung.springbootsecurity;
public class Msg {
    private String title;
    private String content;
    private String extraInfo;
    public Msg() {
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public String getContent() {
        return content;
    }
    public void setContent(String content) {
        this.content = content;
    }
    public String getExtraInfo() {
        return extraInfo;
    }
    public void setExtraInfo(String extraInfo) {
        this.extraInfo = extraInfo;
    }
    public Msg(String title, String content, String extraInfo) {
        this.title = title;
        this.content = content;
        this.extraInfo = extraInfo;
    }
}

#访问数据

package org.cxzc.myyoung.springbootsecurity;
import org.springframework.data.jpa.repository.JpaRepository;
public interface SysUserRepository extends JpaRepository<SysUser
, Long> {
    SysUser findByUsername(String username);
}

这里的访问权限很简单,这里只有一个通过用户名称,查询用户的方法。

#自定义UserDetailService

package org.cxzc.myyoung.springbootsecurity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.
UserDetailsService;
import org.springframework.security.core.userdetails
.UsernameNotFoundException;
public class CustomUserService implements UserDetailsService {
    @Autowired
    SysUserRepository userRepository;
    @Override
    public UserDetails loadUserByUsername(String s) 
    throws UsernameNotFoundException {
        SysUser user = userRepository.findByUsername(s);
        if (user == null) {
            throw new UsernameNotFoundException("用户名不存在");
        }
        System.out.println("s:"+s);
        System.out.println("username:"+user.getUsername()
        +";password:"+user.getPassword());
        return user;
    }
}

1,自定义 这里我们需要重写UserDetailsService接口

2,重写 loadUserByUsername 方法获得用户

3,实现重写接口中的loadUserByUsername方法,通过该方法查询到对应的用户,这里之所以要实现UserDetailsService接口,是因为在Spring Security中我们配置相关参数需要UserDetailsService类型的数据。

#Spring MVC配置

package org.cxzc.myyoung.springbootsecurity;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/login").setViewName("login");
    }
}

1,注册访问/login 转向login.html页面

#Spring Security 配置

package org.cxzc.myyoung.springbootsecurity;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean
    UserDetailsService customUserService() {
        return new CustomUserService();
    }
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(customUserService());
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and().formLogin().loginPage("/login").failureUrl("/login?error").permitAll().and()
                .logout().permitAll();
    }
}
1.首先当我们要自定义Spring Security的时候我们需要继承自
WebSecurityConfigurerAdapter来完成,相关配置重写对应 方法即可。
2.我们在这里注册CustomUserService的Bean,然后通过重写configure
方法添加我们自定义的认证方式。
3.在configure(HttpSecurity http)方法中,我们设置了登录页面,
而且登录页面任何人都可以访问,然后设置了登录失败地址,也设置了注销请求,
注销请求也是任何人都可以访问的。
4.permitAll表示该请求任何人都可以访问,.anyRequest().authenticated(),
表示其他的请求都必须要有权限认证。
5.这里我们可以通过匹配器来匹配路径,比如antMatchers方法,
假设我要管理员才可以访问admin文件夹下的内容,我可以这样来写:
.antMatchers("/admin/**").hasRole("ROLE_ADMIN"),
也可以设置admin文件夹下的文件可以有多个角色来访问,
写法如下:.antMatchers("/admin/**").
hasAnyRole("ROLE_ADMIN","ROLE_USER")
6.可以通过hasIpAddress来指定某一个ip可以访问该资源,
假设只允许访问ip为210.210.210.210的请求获取admin下的资源,
写法如下.antMatchers("/admin/**").hasIpAddress("210.210.210.210")
7.更多的权限控制方式参看下表:

springboot个人博客权限管理模块的开发 spring boot权限控制_spring_06

#创建登录界面

在template文件夹中创建login.html页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <title>登录</title>
    <link rel="stylesheet" th:href="@{css/bootstrap.min.css}"/>
    <link rel="stylesheet" th:href="@{css/signin.css}"/>
    <style type="text/css">
        body {
            padding-top: 50px;
        }
        .starter-template {
            padding: 40px 15px;
            text-align: center;
        }
</style>
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
    <div class="container">
        <div class="navbar-header">
            <a class="navbar-brand" href="#">Spring Security演示</a>
        </div>
        <div id="navbar" class="collapse navbar-collapse">
            <ul class="nav navbar-nav">
                <li><a th:href="@{/}">首页</a></li>
                <li><a th:href="@{http://www.baidu.com}">百度</a></li>
            </ul>
        </div>
    </div>
</nav>
<div class="container">
    <div class="starter-template">
        <p th:if="${param.logout}" class="bg-warning">已注销</p>
        <p th:if="${param.error}" class="bg-danger">有错误,请重试</p>
        <h2>使用账号密码登录</h2>
        <form class="form-signin" role="form" name="form" th:action="@{/login}" action="/login" method="post">
            <div class="form-group">
                <label for="username">账号</label>
                <input type="text" class="form-control" name="username" value="" placeholder="账号"/>
            </div>
            <div class="form-group">
                <label for="password">密码</label>
                <input type="password" class="form-control" name="password" placeholder="密码"/>
            </div>
            <input type="submit" id="login" value="Login" class="btn btn-primary"/>
        </form>
    </div>
</div>
</body>
</html>

界面效果不多说,比较简单,就是一个简单登录界面,失败的提示等

#首页

在template文件夹中创建index.html页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head>
    <meta charset="UTF-8"/>
    <title sec:authentication="name"></title>
    <link rel="stylesheet" th:href="@{css/bootstrap.min.css}"/>
    <style type="text/css">
        body {
            padding-top: 50px;
        }
        .starter-template {
            padding: 40px 15px;
            text-align: center;
        }
</style>
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
    <div class="container">
        <div class="navbar-header">
            <a class="navbar-brand" href="#">Spring Security演示</a>
        </div>
        <div id="navbar" class="collapse navbar-collapse">
            <ul class="nav navbar-nav">
                <li><a th:href="@{/}">首页</a></li>
                <li><a th:href="@{http://www.baidu.com}">百度</a></li>
            </ul>
        </div>
    </div>
</nav>
<div class="container">
    <div class="starter-template">
        <h1 th:text="${msg.title}"></h1>
        <p class="bg-primary" th:text="${msg.content}"></p>
        <div sec:authorize="hasRole('ROLE_ADMIN')">
            <p class="bg-info" th:text="${msg.extraInfo}"></p>
        </div>
        <div sec:authorize="hasRole('ROLE_USER')">
            <p class="bg-info">无更多显示信息</p>
        </div>
        <form th:action="@{/logout}" method="post">
            <input type="submit" class="btn btn-primary" value="注销"/>
        </form>
    </div>
</div>
</body>
</html>

1.在html标签中我们引入的Spring Security

2.通过sec:authentication="name"我们可以获取当前用户名

3.sec:authorize="hasRole('ROLE_ADMIN')表示当前用户角色为ROLE_ADMIN的话显示里边的内容

4.sec:authorize="hasRole('ROLE_USER')表示当前用户角色为ROLE_USER的话显示该DIV里边的内容

#控制器

package org.cxzc.myyoung.springbootsecurity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class HomeController {
    @RequestMapping("/")
    public String index(Model model) {
        Msg msg = new Msg("测试标题", "测试内容", "额外信息,只对管理员显示");
        model.addAttribute("msg", msg);
        return "index";
    }
}

该控制器比较简单,为了首页显示准备数据。

#运行

运行项目,浏览器中输入 http://localhost:8080/自动跳转到http://localhost:8080/login 展示一个登录界面,
 

springboot个人博客权限管理模块的开发 spring boot权限控制_java_07

 

下面分别输入 正确的账号 和 错误的账号,会有不同的结果信息展示。

ok,本篇内容到这里就完成了,如果小伙伴还有疑问,可以 关注我,我们一起进步

参考:
1. 《JavaEE开发的颠覆者 Spring Boot实战》

本案例下载地址:

https:///ProceduralZC/itcxzc/tree/master/springbootsecurity