Spring Boot(十四)Spring Boot中使用Security实现权限控制

你好,【程序职场】专注于:Spring Boot ,微服务 和 前端APP开发,闲暇之余一起聊聊职场规划,个人成长,还能带你一起探索 副业赚钱渠道,在提升技术的同时我们一起交流 敏捷流程 提高工作效率,从技术到管理一步步提升自我!
 
标签:一个执着的职场程序员!

本文是Spring Boot系列的第十四篇,了解前面的文章有助于更好的理解本文:


1.Spring Boot(一)初识Spring Boot框架
2.Spring Boot(二)Spring Boot基本配置
3.Spring Boot(三)Spring Boot自动配置的原理
4.Spring Boot(四)Spring Boot web项目开发
5.Spring Boot(五)Spring Boot web开发项目(2)配置
6.Spring Boot(六)Spring Boot web开发 SSL配置
7.Spring Boot(七)Spring Boot Websocket
8.Spring Boot(八)Spring Boot Websocket实现聊天功能
9.Spring Boot(九)Spring Boot Bootstrap和AngularJS的使用
10.Spring Boot(十)Spring Boot中使用JPA
11.Spring Boot(十一)Spring Boot中使用REST资源输出
12.Spring Boot(十二)Spring Boot中数据库事务的使用
13.Spring Boot(十三)数据缓存的使用


前言

(一). 什么是Spring Security? 

(二). 相关配置

(三). Spring Security实战

上篇文章为大家讲述了 Spring Boot中 数据缓存的使用;本篇文章接着上篇内容继续为大家介绍SpringBoot中 实现权限控制。

(一). 什么是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实战

#创建项目

#数据库配置

我们这里还是通过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.Driverspring.datasource.url=jdbc:mysql://localhost:3306/springbootsecurity?useUnicode=true&characterEncoding=utf-8spring.datasource.username=rootspring.datasource.password=root
logging.level.org.springframework.security=infospring.thymeleaf.cache=falsespring.jpa.hibernate.ddl-auto=updatespring.jpa.show-sql=truespring.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;
@Entitypublic 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 javax.persistence.Id;
@Entitypublic 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) {        this.name = name;    }}

1,ID为自动生成

2,name为角色名称

#数据结构及初始化

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

用户表: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;
@Configurationpublic 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;
@Configurationpublic 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.更多的权限控制方式参看下表:

#创建登录界面

在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;
@Controllerpublic 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 展示一个登录界面,
 

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

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

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

本案例下载地址:

https://github.com/ProceduralZC/itcxzc/tree/master/springbootsecurity

作者:小小蒲公英

猜你喜欢

转载自blog.csdn.net/jianpengxuexikaifa/article/details/104523473