SpringSecurity从入门到关门放狗(二)

———————集中式整合SpringBoot

上一期我们只是写了一个简单的入门demo(简单到有手就会),这一期我们将在之前的基础上加入自定义的登录和授权逻辑。

接下来同学们不要低头或者弯腰捡笔,开冲!

一、资源准备(页面,数据库)

1.首先我们需要准备一个登录页面,放在static/views的包下,我这里写了一个简单的丑陋的登录页面供大家学习使用,注意其中的登录请求路径框架默认为"/login",请求参数username和password的也是框架默认的参数名,这些都可以在配置类里设置,不过没有特殊需要我们一般不做修改。
还有一件事!请求方式必须为post!

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>login</title>
</head>
<body>
<h1 style="text-align: center">登录界面</h1>
<hr/>
<form action="/login" method="post" style="text-align: center;font-size: large" ><p>用户名:<input type="text" name="username"></p><p>密码:<input type="password" name="password"></p><p><button type="submit">登录</button></p>
</form>
</body>
</html>

2.接下来我们来丰富一下资源用于测试权限的控制
(1)准备一些页面放到我们的static/views包下,相关页面大家到文末的gitee中取哈,这里就不贴代码了

(2)首页页面index.html也要丰富一下,可以让我们方便的进行访问资源

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>index</title>
</head>
<body>
<div style="text-align: center"><h1>HELLO! SPRING SECURITY</h1><hr/><p><a href="employee/delete">删除员工</a></p><p><a href="employee/insert">新增员工</a></p><p><a href="employee/select">查询员工</a></p><p><a href="employee/update">修改员工</a></p><hr/><p><a href="finance/delete">删除账务</a></p><p><a href="finance/insert">新增账务</a></p><p><a href="finance/select">查询账务</a></p><p><a href="finance/update">修改账务</a></p><hr/><a href="/logout">登出</a>
</div>
</body>
</html>

3.在controller中我们添加一些方法来接收访问请求去访问这些资源

@RequestMapping("/employee/{type}")
public String employee(@PathVariable("type") String type) {System.out.println("/views/employee/" + type + "Employee");return "/views/employee/"+type+".html";
}@RequestMapping("/finance/{type}")
public String finance(@PathVariable("type") String type) {System.out.println("/views/finance/" + type + "Finance");return "/views/finance/"+type+".html";
}

4.数据库准备:

数据库的建表sql我也会放到gitee中,大家可以拉取执行一下
——我们稍微low一眼表的内容

  • user表中维护了id,用户名,密码,手机号,昵称,状态的字段。其中密码根据实际业务来讲,数据库中都是进行加密过后的,我们这里统一为用BCrypt加密过的‘123’作为密码
  • role表中维护了id,角色名和角色描述,在中间表中我们建立起来用户和角色的关系——“唐伯虎”拥有管理员的角色,“祝枝山”拥有经理的角色。

二、项目添加数据库相关依赖和配置

1.在pom.xml中添加mysql连接驱动和mybatis

 <!-- mysql连接驱动 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!--mybatis--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.0.1</version></dependency>

2.在application.yml中指定数据库配置相关信息,此处根据自己的实际情况来配置

server:port: 8080
spring:application:name: my-spring-security
#指定数据库相关信息datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql:///spring_security?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8username: rootpassword: root
mybatis:mapper-locations: classpath*:com.jerry.springSecurityDemo/*.xmltype-aliases-package: com.jerry.springSecurityDemo.domainconfiguration:map-underscore-to-camel-case: true

3.在启动类上添加扫描注解

@MapperScan("com.jerry.springSecurityDemo.mapper")

三、登录和授权逻辑的代码开发

1.接下来我们来配置spring security。创建一个config包,用于存放我们的配置文件,在包下创建一个SecurityConfig.java文件作为配置类(名字随便,但是要符合命名规范哦),继承WebSecurityConfigurerAdapter类并重写其中的两个方法(见下面的代码)

// 声明这是个spring security的配置类,注解中建议我们继承WebSecurityConfigurerAdapter
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {// 查询用户信息@Autowiredprivate UserService userService;/*用于一个密码加密的Bean,返回值必须是PasswordEncoder这个接口的实现类对象,我们可以通过实现这个接口来写我们需要的加密方式逻辑,这里我们用官方推荐的方式BCryptPasswordEncoder来进行加密(和数据库中密码加密方式相同)*/@Beanpublic PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();};@Overridepublic void configure(AuthenticationManagerBuilder auth) throws Exception {/*// 方式一:认证用户的来源为内存,// 使用这种方式,我们可以通过硬编码的方式将用户名密码角色等信息告知安全框架// 这种方式一般是我们学习或自己测试的时候使用,这里我们就不做过多讲解,// 有兴趣的同学可以根据注释了解一下auth.inMemoryAuthentication()// 用户名.withUser("user")// 1.密码 {noop}表示明文。// 2.不加默认为密文,需要在inMemoryAuthentication()后面// 加.passwordEncode(passwordEncoder())// 括号里为指定密码加密方式.password("{noop}123")// 角色// 区别于xml配置,不需要加ROLE_的前缀.roles("USER").and().withUser("admin").password("{noop}456").roles("ADMIN");*/// 方式二:认证用户的来源为数据库// 使用这种方式,我们可以从数据库中查询用户的信息告知安全框架auth.userDetailsService(userService).passwordEncoder(passwordEncoder());}@Overridepublic void configure(HttpSecurity http) throws Exception {// 设置权限相关http.authorizeRequests()// 放行资源,这里也需要把前端需要的css等前端需要的样式文件放行.antMatchers("/views/login.html","/fail.html").permitAll()// 需要权限访问的资源,注意此处是访问路径不是文件的存放路径!!.antMatchers("/employee/**").hasRole("ADMIN").antMatchers("/finance/**").hasAnyRole("MANAGER","ADMIN")// 其他资源认证后访问.anyRequest().authenticated()// 返回上一层设置对象.and()// 设置登录相关,在里面我们可以配置第一步我们所说的参数名和请求路径.formLogin()// 登录页面.loginPage("/views/login.html")// 登录请求路径.loginProcessingUrl("/login")// 登录成功和失败后跳转的页面,// 登录失败的页面大家自己写一个就行了,注意访问路径要正确/*successForwardUrl()方法报405这是因为SpringMVC中POST方法不支持直接返回页面,用successForwardUrl的话那么就在controller中再重定向就行了或者用defaultSuccessUrl()*//// .successForwardUrl("/.html").defaultSuccessUrl("/")//.failureForwardUrl("/fail.html").failureUrl("/fail.html").and()// 设置登出相关.logout()// 登出路径.logoutUrl("/logout")// 登出成功后跳转的页面,我们跳转到登录页面上.logoutSuccessUrl("/views/login.html")// 登出清空session.invalidateHttpSession(true).and()// 关闭csrf,以允许不携带内部随机生成的token也可以访问资源.csrf().disable();}
}

2.从数据库中取出用户信息,告知给security框架。框架识别的用户对象是类型是UserDetails接口的实现类,权限类型是GrantedAuthority的实现类,而加载用户对象的方法是UserDetailsService接口中的loadUserByUsername方法。
知道了这些,我们就可以将自己查到的用户对象交给框架了。
(1)首先需要写个用户查询业务层接口UserService继承UserDetailsService接口,

public interface UserService extends UserDetailsService {}

(2)通过UserServiceImpl去实现用户查询业务层接口,并重写其中的loadUserByUsername方法。在loadUserByUsername方法中可以直接调用持久层UserMapper去查询用户封装到实体类中。

@Service
public class UserServiceImpl implements UserService {@Autowiredprivate UserMapper userMapper;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {return userMapper.getUserByUsername(username);}
}
@Mapper
public interface UserMapper{UserDo getUserByUsername(String username);
}

(3)这里的用户实体类我们可以自己创建,但是要实现UserDetails接口,并重写其中的方法。
其中的角色实体类也要实现GrantedAuthority接口,并重写其中的方法。

  • 用户实体类:
public class UserDo implements UserDetails{private String id;private String username;private String password;private String mobile;private String nickname;private String status;private List<RoleDo> roles;/*这里我们可以根据查询到的账号状态进行判断返回true或者false*/// 账户是否未过期@JsonIgnore@Overridepublic boolean isAccountNonExpired() {return true;}// 账户是否未锁定@JsonIgnore@Overridepublic boolean isAccountNonLocked() {return true;}// 密码是否为过期@JsonIgnore@Overridepublic boolean isCredentialsNonExpired() {return true;}// 是不是可用@JsonIgnore@Overridepublic boolean isEnabled() {return "1".equals(status);}// 权限集合@JsonIgnore@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return roles;}// 此处我省略了set、get方法,但是大家不要省略哦
  • 角色实体类:
public class RoleDo implements GrantedAuthority {private String id;private String roleName;private String roleDesc;// 获取权限内容@JsonIgnore@Overridepublic String getAuthority() {return roleName;}// 此处我省略了set、get方法,但是大家不要省略哦
}

(4)接下里就是通过xml或者注解来查询用户信息了,这里我用的是xml方式

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jerry.springSecurityDemo.mapper.UserMapper"><resultMap type="com.jerry.springSecurityDemo.domain.UserDo" id="rolesResultMap"><id property="id" column="id"/><result property="username" column="username"/><result property="password" column="password"/><result property="mobile" column="mobile"/><result property="nickname" column="nickname"/><result property="status" column="status"/><collection property="roles" ofType="com.jerry.springSecurityDemo.domain.RoleDo" ><id property="id" column="roleId"/><result property="roleName" column="roleName"/><result property="roleDesc" column="roleDesc"/></collection></resultMap><select id="getUserByUsername" parameterType="string" resultMap="rolesResultMap">select u.id,u.username,u.password,u.mobile,u.nickname,u.status,r.id roleId,r.role_name roleName,r.role_desc roleDescfrom user uleft join user_role ur on u.id = ur.user_idleft join role r on r.id = ur.role_idwhere u.username = #{username}</select></mapper>

以上请注意各种路径是否正确,这里我就不做赘述了。

四、测试

写了这多,终于到了收获的季节了。成败在此一举了!
(1)启动项目,访问localhost:8080

现在显示的就不是框架为我们准备的登录页面,而是我们自定义的登录页面了。

(2)输入我们在数据库中准备的用户名和密码登录测试,我们首先用“祝枝山”的账号进行测试(111111/123)

  • 登录成功

    (2)点击 查询账务 ,访问成功
  • 查询账务

    (3)回退一步,我们来点击 查询员工 ,显示403禁止访问,没有权限访问失败
  • 查询员工
因为此账号只有ROLE_MANAGER的权限,
所以只能访问以finance开头路径的资源,
不能访问以employee开头路径的资源

这里如果想优化一下,给用户一个友好的提示页面,可以在配置类SecurityConfig.java里进行以下配置

配置完毕之后再次启动我们的项目,重复以上操作就会看到我们定义的无权限的界面

我们点击首页的 登出 按钮,换用“唐伯虎”的账号进行测试(000000/123),首页上的资源就可以全部访问了。(截图就省略了,大家自己测试一下吧,我赶着写完歇会~)

到此,项目中基本的授权认证功能就实现了。
呼~(长舒一口气),下一期我们搞点骚的操作,敬请期待吧

gitee:https://gitee.com/yibushao/my_spring_security

SpringSecurity从入门到关门放狗(二)相关推荐

  1. SpringSecurity从入门到关门放狗(一)

    SpringSecurity从入门到关门放狗(一) -------集中式整合SpringBoot 你能看到这篇文章,说明你已经对spring security有了一定的了解.如果不了解也咩关系,知道这 ...

  2. 这可能是最好的RxJava 2.x 入门教程(二)

    这可能是最好的 RxJava 2.x 入门教程系列专栏 文章链接: 这可能是最好的 RxJava 2.x 入门教程(完结版)[推荐直接看这个] 这可能是最好的RxJava 2.x 入门教程(一) 这可 ...

  3. Python 简单入门指北(二)

    Python 简单入门指北(二) 2 函数 2.1 函数是一等公民 一等公民指的是 Python 的函数能够动态创建,能赋值给别的变量,能作为参传给函数,也能作为函数的返回值.总而言之,函数和普通变量 ...

  4. [转]OllyDBG 入门系列(二)-字串参考

    标 题: [原创]OllyDBG 入门系列(二)-字串参考 作 者: CCDebuger 时 间: 2006-02-14,13:34 链 接: http://bbs.pediy.com/showthr ...

  5. STM32F103五分钟入门系列(二)GPIO的七大寄存器+GPIOx_LCKR作用和配置

    摘自:STM32F103五分钟入门系列(二)GPIO的七大寄存器+GPIOx_LCKR作用和配置 作者:自信且爱笑' 发布时间: 2021-05-01 12:08:32 网址:https://blog ...

  6. Oracle入门(十二)之SQL的DDL

    一.数据类型 Character 数据类型 Number 数据类型 Date 数据类型 Raw 和 Long Raw 数据类型 LOB 数据类型 注:Oracle数据类型详解 二.表 (1)创建表 c ...

  7. WPF入门教程系列(二) 深入剖析WPF Binding的使用方法

    同一个对象(特指System.Windows.DependencyObject的子类)的同一种属性(特指DependencyProperty)只能拥有一个binding. 这一点可以通过设置bindi ...

  8. OllyDBG 入门系列(二)-字串参考

    标 题: [原创]OllyDBG 入门系列(二)-字串参考 作 者: CCDebuger 时 间: 2006-02-14,13:34:43 链 接: http://bbs.pediy.com/show ...

  9. 【Python】Python实战从入门到精通之二 -- 教你使用Python中列表操作

    本文是Python实战–从入门到精通系列的第二篇文章: [Python]Python实战从入门到精通之一 – 教你深入理解Python中的变量和数据类型 Python实战从入门到精通之二 – 教你使用 ...

最新文章

  1. OpenStack环境搭建(四:web控制端各节点的部署及配置)
  2. OpenMP在ARM-Linux以及NDK中的编译和使用
  3. windows 安装 reviewboard
  4. OperationalError: (1044, Access denied for user ''@'localhost' to database 'mydb')
  5. python中%符号详解
  6. dm9000AE调试记录
  7. CF1286D-LCC【动态dp,数学期望】
  8. python读取大文件的坑_如何在Python中读取大文件的特定部分
  9. java 读取txt字符串_java读取txt并获取某一部分字符串
  10. php可以单干吗_拉伸膜包装机适合包装牛肉干吗?
  11. 定制操作(传递函数或lambda表达式)
  12. 前端开发-Weex初试
  13. Linux 3.13.0删除了Netlink API函数genl_register_ops() / genl_unregister_ops()
  14. OpenSocial版的51虚拟支付--ROCKYOU
  15. win10注册表的备份与恢复;对“未将所有数据都成功写入到注册表中。某些项是由系统或其他进程打开的,或者你没有足够的权限执行此操作”问题的理解
  16. js除法保留小数_javascript(js)的小数点乘法除法问题详解
  17. 阿里api网关接口客户端demo,java实现源码,其他语言可参考
  18. RSF 分布式服务框架设计
  19. 爱奇艺自主研发的动态化框架!
  20. 融云发布公告:五大高级功能将全面开放

热门文章

  1. 练好大数据内功,企业需要这样的全链路方案
  2. DTOJ 4878. 零一树
  3. 移动端浏览器之兼容性问题总结
  4. 简单线性回归截距假设检验_第10章 简单线性回归分析思考与练习参考答案
  5. 电脑强制关机导致本地mysql无法启动_本地mysql因为电脑突然重启造成了mysql无法启动...
  6. stm32数码管显示数字 流程图_怎样用keil编写4位数码管显示1234的程序
  7. 02.Javascript中的继承----Inherits
  8. 数字音乐版权的保护问题
  9. firewalld标准规则
  10. 【哈佛积极心理学笔记】第9讲 积极情绪