SpringSecufity 基础总结

本文详细介绍了Spring Security框架的使用,从基础配置到实际应用,包括认证、授权、自定义登录页面、记住我功能等。通过实验展示了如何集成Spring Security到Web应用中,实现用户权限控制和安全功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、简介

https://docs.spring.io/spring-security/site/docs/4.2.10.RELEASE/guides/html5/helloworld-xml.html

SpringSecurity融合Spring技术栈,提供JavaEE应 用的整体安全解决方案;

Spring Security为基于Java EE的企业软件应用提供全面的安全服务。

Spring Security只需要少量配置,就能构建一个强大的安全的应用系统。

目前市面上受欢迎的两个安全框架:Apache Shiro、SpringSecurity;

SpringSecurity可以无缝整合Spring应用,具有强大的自动化web安全管控功能。而Shiro是一个轻量级强大的安全框架,可以脱离web应用来提供安全管控,但是对于web的一些定制安全需要手动编写;SpringBoot底层默认整合SpringSecurity作为安全框架,所以我们推荐web应用使用SpringSecurity来控制安全;

1、文档

Hello Spring Security <https://github.com/spring-projects/spring-security/tree/4.2.10.RELEASE/samples/javaconfig/helloworld > 基于Java配置整合示例

Hello Spring Security Boot https://github.com/spring-projects/spring-security/tree/4.2.10.RELEASE/samples/boot/helloworld 与SpringBoot整合案例

Hello Spring Security XML https://github.com/spring-projects/spring-security/tree/4.2.10.RELEASE/samples/xml/helloworld 基于XML方式整合示例

Hello Spring MVC Security https://github.com/spring-projects/spring-security/tree/4.2.10.RELEASE/samples/javaconfig/hellomvc SpringMVC集成示例

Custom Login Form https://github.com/spring-projects/spring-security/tree/4.2.10.RELEASE/samples/javaconfig/form 自定义登录表单示例

2、使用方式

  • 一种是全部利用配置文件,将用户、权限、资源(url)硬编码在xml文件中

  • 二种是用户和权限用数据库存储,而资源(url)和权限的对应采用硬编码配置

  • 三种是细分角色和权限,并将用户、角色、权限和资源均采用数据库存储,并且自定义过滤器,代替原有的FilterSecurityInterceptor过滤器, 并分别实现AccessDecisionManager、InvocationSecurityMetadataSourceService和UserDetailsService,并在配置文件中进行相应配置。

  • 四是修改springsecurity的源代码,主要是修改InvocationSecurityMetadataSourceService和UserDetailsService两个类。

    1. InvocationSecurityMetadataSourceService

      将配置文件或数据库中存储的资源(url)提取出来加工成为url和权限列表的Map供Security使用

    2. UserDetailsService

​ 提取用户名和权限组成一个完整的(UserDetails)User对象,该对象可以提供用户的详细信息供AuthentationManager进行认证与授权使用

3、概念

认证

authenfication: 身份验证

​ “身份验证” 是指建立主体(principal)的过程,主体就是他们声称的是谁 (“主体” 通常指用户、设备或在应用程序中可以执行动作的其他系统)。也就是 “证明你是谁”。

授权

authorization: 授权

​ “授权” 是指确定主体(principal) 是否被允许执行系统中某个动作的过程。也就是 “你能做什么!”

为了达到“授权”决策(安全框架决定你是否有权限做此事),“身份验证”(authentication)过程已经建立了主体的身份(Principal)

二、SpringSecurity-HellWorld

1、测试环境搭建

1.1 创建普通的maven-war 工程:spring-security-helloworld

注意:以下所有的测试都采用的 Idea 集成开发工具

pom 文件增加依赖

 <dependencies>
     <dependency>
         <groupId>org.springframework</groupId>
         <artifactId>spring-webmvc</artifactId>
         <version>4.3.20.RELEASE</version>
     </dependency>
     <dependency>
         <groupId>javax.servlet.jsp</groupId>
         <artifactId>jsp-api</artifactId>
         <version>2.2</version>
         <scope>provided</scope>
     </dependency>
     <dependency>
         <groupId>javax.servlet</groupId>
         <artifactId>servlet-api</artifactId>
         <version>2.5</version>
         <scope>provided</scope>
     </dependency>
     <dependency>
         <groupId>javax.servlet</groupId>
         <artifactId>jstl</artifactId>
         <version>1.2</version>
     </dependency>
</dependencies>

web.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>DispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

spring配置:spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <context:component-scan base-package="com.oy.security"></context:component-scan>

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/views/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>

    <mvc:annotation-driven/>
    <mvc:default-servlet-handler/>
</beans>

导入实验资源

  • 需要资源的博客末尾有下载地址
  • 导入页面

image-20210203111400600

  • 导入 controller

image-20210203165335907

运行测试

image-20210203111542765

2、引入SpringSecurity框架

2.1 添加 security-pom 依赖

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>4.2.10.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>4.2.10.RELEASE</version>
</dependency>
<!-- 标签库 -->
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-taglibs</artifactId>
    <version>4.2.10.RELEASE</version>
</dependency>

2.2 web.xml 中添加SpringSecurity 的 Filter 进行安全控制

<filter>
    <filter-name>springSecurityFilterChain</filter-name><!--名称固定,不能变 -->
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

2.3 加入SpringSecurity配置类

  • @Configuration、@Bean 注解作用
@Configuration //声明当前类是一个配置类。相当与XML配置文件作用。
@EnableWebSecurity  //声明式配置,启用SpringSecurity安全机制。
public class AppWebSecurityConfig extends WebSecurityConfigurerAdapter {
    
}

2.4 启动测试效果

image-20210203165802954

  • 所有资源访问受限(包括静态资源)
  • 框架自带一个默认的登录界面
  • 账号密码错误会有提示

image-20210203170522858

  • 查看登录页面的源码,发现有个hidden-input: name = “_csrf” 这个是springsecurity 帮我们防止“跨站请求伪造”攻击;还可以防止表单重复提交。

image-20210203170641242

三、SpringSecurity 实验

1、实验一: 授权首页和静态资源

  • 配置类 (AppWebSecurityConfig extends WebSecurityConfigurerAdapter
  • 重写configure(HttpSecurity http)方法
@Configuration // 声明当前类是一个配置类。相当于XML配置文件作用。
@EnableWebSecurity  //声明式配置,启用SpringSecurity安全机制
public class AppWebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //super.configure(http); // 取消默认配置
        http.authorizeRequests()
                // 设置匹配的资源放行
                .antMatchers("/layui/**","/index.jsp").permitAll()
                // 剩余任何资源必须认证
                .anyRequest().authenticated();
    }
}
  • 测试结果

静态资源和 index.jsp 都可以访问

image-20210203171903224

不存在的资源

  1. 有权限无资源 400

image-20210203172202192

  1. 无权限

image-20210203172242911

2、实验二: 默认及自定义登录页

@Override
protected void configure(HttpSecurity http) throws Exception {
    //super.configure(http); // 取消默认配置
    http.authorizeRequests()
        // 设置匹配的资源放行
        .antMatchers("/layui/**","/index.jsp").permitAll()
        // 剩余任何资源必须认证
        .anyRequest().authenticated();

    // 实验二: 默认及自定义登录页
    http.formLogin(); // 默认登录页

}
  • http://localhost:8080/SpringSecurity/level3/3

image-20210203174330647

@Override
protected void configure(HttpSecurity http) throws Exception {
    //super.configure(http); // 取消默认配置
    http.authorizeRequests()
        // 设置匹配的资源放行
        .antMatchers("/layui/**","/index.jsp").permitAll()
        // 剩余任何资源必须认证
        .anyRequest().authenticated();

    // 实验二: 默认及自定义登录页

    // 自定义登录页
    http.formLogin().loginPage("/index.jsp");

}

​ 在访问没有权限资源或页面时,将会自动跳转到index.jsp登录页面

  • 在测试的时候需要先暂时_csrf这个功能
http.csrf().disable();

3、实验三: 自定义表单登录逻辑分析

@Override
protected void configure(HttpSecurity http) throws Exception {
    //super.configure(http); // 取消默认配置
    http.authorizeRequests()
        // 设置匹配的资源放行
        .antMatchers("/layui/**","/index.jsp").permitAll()
        // 剩余任何资源必须认证
        .anyRequest().authenticated();

    // 自定义登录页
    http.formLogin().loginPage("/index.jsp")
        .loginProcessingUrl("/index.jsp")
        .usernameParameter("loginacct")
        .passwordParameter("userpswd")
        .defaultSuccessUrl("/main.html");
    http.csrf().disable(); // 禁用csrf

}
  • 表单提交地址:${PATH }/index.jsp

  • 表单提交请求方式:post

  • 提交表单:

    • 引入jquery: <script src="${PATH }/layui/jquery.min.js"></script>
    • $(“form”).submit();

image-20210203181846728

image-20210203181905444

如果没有关闭 CSRF, 提交请求被拒绝, 需要暂时禁用csrf:http.csrf().disable();

image-20210203181950686

  • 表单提交请求失败,提取错误消息:${SPRING_SECURITY_LAST_EXCEPTION.message}
<div class="layadmin-user-login-box layadmin-user-login-header">
	<h2>layuiAdmin</h2>
	<p>layui 官方出品的单页面后台管理模板系统</p>
	<p>${SPRING_SECURITY_LAST_EXCEPTION.message}</p>
</div>

image-20210203182702663

4、实验四: 自定义认证用户信息

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    // 默认认证
    //super.configure(auth);


    //实验四:自定义认证用户信息 - 基于内存认证方式
    auth.inMemoryAuthentication()
        .withUser("zhangsan").password("123456").roles("学徒","大师")
        .and()
        .withUser("lisi").password("123456").authorities("罗汉拳","武当长拳");
}

​ 输入用户名和密码之后一致,就可以登录页面后台,前提是与自己基于内存认证方式一致的用户名和密码

image-20210203183705870

  • CSRF 跨站请求伪造
  • SpringSecurity 添加了 crsf 功能 【DefaultCsrfToken】,所有的表单提交为了防止跨站请求的伪造,我们需要加上_csrf项;或者,暂时禁用 http.csrf().disable();
// 可以在页面任意位置加,没有唯一性
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>

image-20210203221217981

5、实验五:用户注销完成

@Override
protected void configure(HttpSecurity http) throws Exception {
    //super.configure(http); // 取消默认配置
    http.authorizeRequests()
        // 设置匹配的资源放行
        .antMatchers("/layui/**","/index.jsp").permitAll()
        // 剩余任何资源必须认证
        .anyRequest().authenticated();

    //http.logout(); //默认注销请求  请求路径:"/logout"
    http.logout().logoutUrl("/logout").logoutSuccessUrl("/index.jsp");


}
<li class="layui-nav-item">
    <form id="logout" action="${PATH}/logout" method="post">
        <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}">
        <a onclick="$('#logout').submit()">退出</a>
    </form>
</li>

image-20210203222857350

  • /logout: 退出系统

  • 如果csrf 开启,必须post、方式的/logout 请求,表单中需要增加 csrf token

  • logoutUrl();退出系统需要发送的请求

  • logoutSuccessUrl();退出系统成功以后要跳转的页面地址

  • addLogoutHandler():自定义注销处理器

  • deleteCookies():指定需要删除的cookie

  • invalidateHttpSession():session失效(DEBUG)

6、实验六:基于角色的访问控制

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
        // 允许所有人都访问静态资源
        .antMatchers("/layui/**","/index.jsp").permitAll()
        .antMatchers("/level1/**").hasRole("学徒")
        .antMatchers("/level2/**").hasRole("大师")
        .antMatchers("/level3/**").hasRole("宗师")
        // 放置最后,以上没有规定的都需要权限认证
        .anyRequest().authenticated();
}

注意:

1.  将.anyRequest().authenticated()错误的设置在前面,后面的设置就不起作用了。
2.  设置所有, "/**" 都可以访问,其他再进行的设置就不会起作用了
3.  设置匿名访问/level3/** 可以不用登录,匿名访问:`.anyRequest().anonymous();`

拥有该角色的资源可以访问,否则不可以访问

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    // 默认认证
    //super.configure(auth);


    //实验四:自定义认证用户信息 - 基于内存认证方式
    auth.inMemoryAuthentication()
        .withUser("zhangsan").password("123456").roles("学徒","大师")
        .and()
        .withUser("自定义访问拒绝处理页面,lisi").password("111111").authorities("USER","MANGER");
    	// .withUser("lisi").password("123456").authorities("罗汉拳","武当长拳");
}

7、实验七:自定义访问拒绝处理页面

直接增加处理映射界面

http.exceptionHandling().accessDeniedPage("/unauth.html");

在控制器类中增加映射处理

@RequestMapping("/unauth.html")
public String unauth(){
    return "unauth";
}

增加显示页面,将main.jsp复制,命名为unauth.jsp,增加一句提示信息

<h1>你无权访问该页面...</h1>

image-20210203225722734

测试显示效果

image-20210203225758442

8、实验八:记住我功能

8.1 记住我功能-免登录原理
http.rememberMe();

默认规则

  • 页面checkbox提交 remember-me参数

image-20210203230539852

  • 默认记住2周登录状态:AbstractRememberMeServices

image-20210203230712762

  • 会在cookie中保存名为:remember-me的cookie

image-20210203230802123

  • 登录后页面,关闭浏览器,直接访问:http://localhost:8080/SpringSecurity/main.html 可以成功访问,不必登录。
  • 这种方式,token值是放置在内存中的,服务器端重启tomcat,token会失效。需要将token记录在数据库持久化才不会失效。
8.2 记住我-数据版

引入pom.xml 包

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-orm</artifactId>
    <version>4.3.20.RELEASE</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.12</version>
</dependency>
<!-- mysql驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.47</version>
</dependency>

配置数据源

<!-- 配置数据源 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="username" value="root"></property>
    <property name="password" value="root"></property>
    <property name="url" value="jdbc:mysql://localhost:3306/security?useSSL=false"></property>
    <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
</bean>
<!--  jdbcTemplate-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource"></property>
</bean>

创建表

create table persistent_logins (
    username varchar(64) not null, 
    series varchar(64) primary key,
    token varchar(64) not null, 
    last_used timestamp not null
);

image-20210203231619271

image-20210203231727138

设置记住我

@Autowired
DataSource dataSource;

@Override
protected void configure(HttpSecurity http) throws Exception {
    //记住我
    JdbcTokenRepositoryImpl ptr = new JdbcTokenRepositoryImpl();
    ptr.setDataSource(dataSource);
    http.rememberMe().tokenRepository(ptr);
}

image-20210203232100434

image-20210203232126591

四、认证

1、自定义UserDatailsService检索用户

创建表结构

CREATE TABLE `t_admin` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `loginacct` varchar(255) NOT NULL,
  `userpswd` char(32) NOT NULL,
  `username` varchar(255) NOT NULL,
  `email` varchar(255) NOT NULL,
  `createtime` char(19) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8 COMMENT='';

insert  into `t_admin`(`id`,`loginacct`,`userpswd`,`username`,`email`,`createtime`) values (1,'superadmin','e10adc3949ba59abbe56e057f20f883e','超级管理员','admin@atguigu.com','2019-01-12 17:18:00'),(3,'lisi','e10adc3949ba59abbe56e057f20f883e','lisi','lisi@atguigu.com','2019-01-12 17:18:00'),(4,'wangwu','f1887d3f9e6ee7a32fe5e76f4ab80d63','wangwu','wangwu@163.com','2019-01-12 17:18:00'),(8,'aaa','123456','aaa','aaa@atguigu.com','2019-01-12 17:18:00'),(12,'xxxx','e10adc3949ba59abbe56e057f20f883e','xxxx','xxxx@163.com','2019-01-21 10:54:36'),(13,'yy','e10adc3949ba59abbe56e057f20f883e','yy','yy@atguigu.com','2019-01-21 10:56:49'),(14,'qqq','e10adc3949ba59abbe56e057f20f883e','qqqq','qqq@atguigu.com','2019-01-21 11:00:01'),(15,'qqq456','e10adc3949ba59abbe56e057f20f883e','测试中文123','qqq654@atguigu.com','2019-01-21 11:15:53');

CREATE TABLE `t_menu` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `pid` int(11) DEFAULT NULL,
  `name` varchar(255) DEFAULT NULL,
  `icon` varchar(255) DEFAULT NULL,
  `url` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8 COMMENT='';

insert  into `t_menu`(`id`,`pid`,`name`,`icon`,`url`) values (1,0,'控制面板','glyphicon glyphicon-dashboard','main.html'),(2,0,'权限管理','glyphicon glyphicon glyphicon-tasks',NULL),(3,2,'用户维护','glyphicon glyphicon-user','admin/index.html'),(4,2,'角色维护','glyphicon glyphicon-king','role/index.html'),(5,2,'权限维护','glyphicon glyphicon-lock','permission/index.html'),(6,2,'菜单维护','glyphicon glyphicon-th-list','menu/index.html'),(7,0,'业务审核','glyphicon glyphicon-ok',NULL),(8,7,'实名认证审核','glyphicon glyphicon-check','auth_cert/index.html'),(9,7,'广告审核','glyphicon glyphicon-check','auth_adv/index.html'),(10,7,'项目审核','glyphicon glyphicon-check','auth_project/index.html'),(11,0,'业务管理','glyphicon glyphicon-th-large',NULL),(12,11,'资质维护','glyphicon glyphicon-picture','cert/index.html'),(13,11,'分类管理','glyphicon glyphicon-equalizer','certtype/index.html'),(14,11,'流程管理','glyphicon glyphicon-random','process/index.html'),(15,11,'广告管理','glyphicon glyphicon-hdd','advert/index.html'),(16,11,'消息模板','glyphicon glyphicon-comment','message/index.html'),(17,11,'项目分类','glyphicon glyphicon-list','projectType/index.html'),(18,11,'项目标签','glyphicon glyphicon-tags','tag/index.html'),(19,0,'参数管理','glyphicon glyphicon-list-alt','param/index.html');

CREATE TABLE `t_permission` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '',
  `name` varchar(255) DEFAULT NULL COMMENT '',
  `title` varchar(255) DEFAULT NULL COMMENT '',
  `icon` varchar(255) DEFAULT NULL,
  `pid` int(11) DEFAULT NULL COMMENT '',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8 COMMENT='';

insert  into `t_permission`(`id`,`name`,`title`,`icon`,`pid`) values (1,NULL,'用户模块','glyphicon glyphicon-user',0),(2,'user:add','新增','glyphicon glyphicon-plus',1),(3,'user:delete','删除','glyphicon glyphicon-remove',1),(4,'user:update','更新','glyphicon glyphicon-pencil',1),(5,'user:get','查询','glyphicon glyphicon-zoom-in',1),(6,'user:assign:role','授予角色','glyphicon glyphicon-user',1),(7,NULL,'角色模块','glyphicon glyphicon-heart',0),(8,'role:add','新增','glyphicon glyphicon-plus',7),(9,'role:delete','删除','glyphicon glyphicon-remove',7),(10,'role:get','查询','glyphicon glyphicon-zoom-in',7),(11,'role:update','修改','glyphicon glyphicon-pencil',7),(12,'role:assign:permission','授予权限','glyphicon glyphicon-user',7),(13,NULL,'菜单模块','glyphicon glyphicon-th-list',0),(14,'menu:add','新增','glyphicon glyphicon-plus',13),(15,'menu:delete','删除','glyphicon glyphicon-remove',13),(16,'menu:update','修改','glyphicon glyphicon-pencil',13),(17,'menu:get','查询','glyphicon glyphicon-zoom-in',13),(18,'menu:assign:permission','授予权限','glyphicon glyphicon-user',13);

CREATE TABLE `t_role` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=44 DEFAULT CHARSET=utf8 COMMENT='';

insert  into `t_role`(`id`,`name`) values (1,'PM - 项目经理'),(2,'SE - 软件工程师'),(3,'PG - 程序员'),(4,'TL - 组长'),(5,'GL - 组长'),(6,'QA - 品质保证'),(7,'QC - 品质控制'),(8,'SA - 软件架构师'),(9,'CMO / CMS - 配置管理员'),(10,'测试工程师'),(11,'审批人员');

CREATE TABLE `t_role_permission` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `roleid` int(11) DEFAULT NULL,
  `permissionid` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `FK_Reference_3` (`roleid`),
  KEY `FK_Reference_4` (`permissionid`),
  CONSTRAINT `FK_Reference_4` FOREIGN KEY (`permissionid`) REFERENCES `t_permission` (`id`),
  CONSTRAINT `FK_Reference_3` FOREIGN KEY (`roleid`) REFERENCES `t_role` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8 COMMENT='';

insert  into `t_role_permission`(`id`,`roleid`,`permissionid`) values (7,1,1),(8,1,2),(9,1,6),(10,1,13),(11,1,16),(12,1,17),(13,1,18);

CREATE TABLE `t_admin_role` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `adminid` int(11) DEFAULT NULL,
  `roleid` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `FK_Reference_1` (`adminid`),
  KEY `FK_Reference_2` (`roleid`),
  CONSTRAINT `FK_Reference_2` FOREIGN KEY (`roleid`) REFERENCES `t_role` (`id`),
  CONSTRAINT `FK_Reference_1` FOREIGN KEY (`adminid`) REFERENCES `t_admin` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8 COMMENT='';

insert  into `t_admin_role`(`id`,`adminid`,`roleid`) values (2,1,2),(9,1,4),(11,1,8),(12,15,1),(13,15,2),(14,15,3);


CREATE TABLE `t_permission_menu` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `menuid` int(11) DEFAULT NULL,
  `permissionid` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `FK_Reference_10` (`menuid`),
  KEY `FK_Reference_9` (`permissionid`),
  CONSTRAINT `FK_Reference_10` FOREIGN KEY (`menuid`) REFERENCES `t_menu` (`id`),
  CONSTRAINT `FK_Reference_9` FOREIGN KEY (`permissionid`) REFERENCES `t_permission` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8 COMMENT='';

insert  into `t_permission_menu`(`id`,`menuid`,`permissionid`) values (7,2,1),(8,2,2),(9,2,3),(10,2,4),(11,2,5),(12,2,6),(13,2,7),(14,2,8),(15,2,9),(16,2,10),(17,2,11),(18,2,12);

配置 configure(AuthenticationManagerBuilder auth)

@Autowired
UserDetailsService userDetailsService;//用户详情查询服务组件的接口

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
	//根据用户名查询出用户的详细信息
	auth.userDetailsService(userDetailsService); 
}

编写UserDetailService实现

image-20210204160515382

@Component
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    JdbcTemplate jdbcTemplate;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        String queryUser = "SELECT * FROM `t_admin` WHERE loginacct=?";

        //1、查询指定用户的信息
        Map<String, Object> map = jdbcTemplate.queryForMap(queryUser, username);

        //2、将查询到的用户封装到框架使用的UserDetails里面
        return new User(map.get("loginacct").toString(), map.get("userpswd").toString(),
                AuthorityUtils.createAuthorityList("ADMIN", "USER"));//暂时写死,过后数据库中查
    }

}

运行测试结果,密码不一致,跳转到登录页,并提示错误消息

image-20210204160556591

2、基于数据库(MD5密码)认证

2.1 配置 configure(AuthenticationManagerBuilder auth)
@Autowired
UserDetailsService userDetailsService;//用户详情查询服务组件的接口

@Autowired
PasswordEncoder passwordEncoder;

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
	auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
}
2.2 引入MD5加密工具类:MD5Util.java
package com.oy.security.component;

import java.security.MessageDigest;

/**
 * MD5算法 哈希算法 MD5算法具有以下特点: 1、压缩性:任意长度的数据,算出的MD5值长度都是固定的。 2、容易计算:从原数据计算出MD5值很容易。
 * 3、抗修改性:对原数据进行任何改动,哪怕只修改1个字节,所得到的MD5值都有很大区别。
 * 4、强抗碰撞:已知原数据和其MD5值,想找到一个具有相同MD5值的数据(即伪造数据)是非常困难的。
 */
public class MD5Util {
	public static String digest16(String inStr) {
		return digest(inStr, 16); 
	}

	public static String digest(String inStr) {
		return digest(inStr, 32);
	}

	private static String digest(String inStr, int rang) {
		MessageDigest md5 = null;
		if (StringUtil.isEmpty(inStr)) {
			return "";
		}

		try {
			md5 = MessageDigest.getInstance("MD5"); // 取得算法
		} catch (Exception e) {
			e.printStackTrace();
			return "";
		}

		char[] charArray = inStr.toCharArray();
		byte[] byteArray = new byte[charArray.length];

		for (int i = 0; i < charArray.length; i++) {
			byteArray[i] = (byte) charArray[i];
		}

		byte[] md5Bytes = md5.digest(byteArray); // 加密

		StringBuilder hexValue = new StringBuilder();

		for (int i = 0; i < md5Bytes.length; i++) {
			int val = ((int) md5Bytes[i]) & 0xff;
			if (val < 16)
				hexValue.append("0");
			hexValue.append(Integer.toHexString(val));
		}
		if (rang == 32) {
			return hexValue.toString();
		} else {
			return hexValue.toString().substring(8, 24);// 转换为32位字符串
		}
	}

	public static void main(String args[]) {
		String s = new String("123456");
		System.out.println(digest(s));
	}
}
2.3 PasswordEncoder接口实现类:PasswordEncoderImpl
@Service
public class PasswordEncoderImpl implements PasswordEncoder {

    /**
     *  密码加密的算法
     * @param charSequence
     * @return
     */
    @Override
    public String encode(CharSequence charSequence) {
        return MD5Util.digest(charSequence.toString());
    }

    /**
     * 比较登录密码和数据库存储密码是否一致
     * @param charSequence 页面的明文密码
     * @param s 数据库的密文密码
     * @return
     */

    @Override
    public boolean matches(CharSequence charSequence, String s) {
        return s.equals(MD5Util.digest(charSequence.toString()));
    }
}

3、基于数据库(BCryptPasswordEncoder)密码加密认证

3.1 PasswordEncoder接口

image-20210204163302959

3.2 使用BCryptPasswordEncoder进行密码加密
 /**
  * 推荐密码加密器用这个 BCryptPasswordEncoder; 将一个字符串加密成一个永不重复的密文
  */
// 1、加盐+加随机数
auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
3.3 本地测试
public class BCryptPasswordEncoderTest {

    @Test
    public void test(){
        BCryptPasswordEncoder pe = new BCryptPasswordEncoder();

        String encode = pe.encode("123456");
        System.out.println(encode);

        // 1. $2a$10$9MeQiXyoq.DVWmdNPC7/TuiwOPCSmeT0xLbMWkUrbAF5.Pnq2mdEK
        // 2. $2a$10$gqjnDfOJ/c9Stvz6pQFXLOHxIQzSpOQXgcgJxpsMgglAtDrMY2Kda
        // 3. $2a$10$tnjbeb0J1vUdynJeu.IYiu5TPQwmrBUiYa0pfgIZIIuQ3r4yIt/ea
    }
}
3.4 服务器运行测试

将生成的密文存储到数据库中(注意:userpswd字段长度)把userpaswd 字段长度扩大,重新启动服务器进行测试。

image-20210204164520586

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值