SpringSecurity框架用法简介

1. SpringSecurity 框架用法简介

在这里插入图片描述
用户登录系统时我们协助 SpringSecurity 把用户对应的角色、权限组装好,同时把各个资源所要求的权限信息设定好,剩下的“登录验证”、“权限验证”等等工作都交给
SpringSecurity

2. 权限管理过程中的相关概念

2.1 主体

英文单词:principal
使用系统的用户或设备或从其他系统远程登录的用户等等。简单说就是谁使用系统谁就是主体。

2.2 认证

英文单词:authentication
权限管理系统确认一个主体的身份,允许主体进入系统。简单说就是“主体”证明自己是谁。
笼统的认为就是以前所做的登录操作。

2.3 授权

英文单词:authorization
将操作系统的“权力”“授予”“主体”,这样主体就具备了操作系统中特定功能的能力。
所以简单来说,授权就是给用户分配权限。
在这里插入图片描述

3. 权限管理的主流框架

3.1 SpringSecurity

Spring 技术栈的组成部分。
在这里插入图片描述
通过提供完整可扩展的认证和授权支持保护你的应用程序。
https://spring.io/projects/spring-security

SpringSecurity 特点:

和 Spring 无缝整合。

全面的权限控制。

专门为 Web 开发而设计。

旧版本不能脱离 Web 环境使用。新版本对整个框架进行了分层抽取,分成了核心模块和 Web 模块。单独引入核心模块就可以脱离 Web 环境。

重量级。

3.2 Shiro

Apache 旗下的轻量级权限控制框架。
在这里插入图片描述
特点:
轻量级。Shiro 主张的理念是把复杂的事情变简单。针对对性能有更高要求的
互联网应用有更好表现。
通用性。
好处:不局限于 Web 环境,可以脱离 Web 环境使用。
缺陷:在 Web 环境下一些特定的需求需要手动编写代码定制。
官网网址:http://shiro.apache.org/
学习视频网址:http://www.gulixueyuan.com/course/45

4. 使用配置类代替 XML 配置文件

4.1 @Configuration 注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component//由于当前注解带有@Component 注解,所以标记当前注解的类可以享受包的
自动扫描
public @interface Configuration {
/**
* Explicitly specify the name of the Spring bean definition associated
* with this Configuration class.
If left unspecified (the common case),
* a bean name will be automatically generated.
*
* <p>The custom name applies only if the Configuration class is picked up via
*
component
scanning
or
supplied
directly
to
a
{@link
AnnotationConfigApplicationContext}.
* If the Configuration class is registered as a traditional XML bean definition,
* the name/id of the bean element will take precedence.
*
* @return the specified bean name, if any
* @see org.springframework.beans.factory.support.DefaultBeanNameGenerator
*/
String value() default "";
}

类标记了这个注解就可以使用这个类代替 Spring 的 XML 配置文件。

4.2 @Bean 注解

用来代替 XML 配置文件中的 bean 标签。下面两种形式效果一致:

<bean id="empHandler" class="com.atguigu.component.EmpHandler">
<property />
</bean>
@Configuration
public class AnnotaionConfig{
@Bean
public EmpHandler getEmpHandler(){
return new EmpHandler();
}
}

提示:Spring 通过调用标记了@Bean 注解的方法将对象放入 IOC 容器行为不会重复调用方法。原因是 Spring 想要获取 bean 对应的实例对象时会查看 IOC 容器中是否已经有了这个对象,如果有则不会执行这个方法,从而保证这个 bean 是单一实例的。
如果希望对应的 bean 是多实例的,则可以配合@Scope 注解

5. HelloWorld 工程创建步骤

5.1 创建 Maven 的 Web 工程

pro05-spring-security
		grouold: com.atguigu.security
		artifactld:pro05-spring-security
		packaging: war

5.2 加入 SpringMVC 环境需要的依赖

<dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>4.3.20.RELEASE</version>
        </dependency>
        <!-- 引入 Servlet 容器中相关依赖 -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>
        <!-- JSP 页面使用的依赖 -->
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.1.3-b06</version>
            <scope>provided</scope>
        </dependency>

5.3 创建 SpringMVC 配置文件

在这里插入图片描述

<?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-4.3.xsd
       http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
">
<context:component-scan
            base-package="com.atguigu.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:annotation-driven>
    <mvc:default-servlet-handler />
</Beans>

5.4 在 web.xml 中配置 DispatcherServlet

<?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">

    <!-- The front controller of this Spring Web application, responsible for handling all application
requests -->
    <servlet>
        <servlet-name>springDispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-mvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <!-- Map all requests to the DispatcherServlet for handling -->
    <servlet-mapping>
        <servlet-name>springDispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    </web-app>

5.5 创建包

com.atguigu.security.controller

5.6 从例子工程中复制 Controller

在这里插入图片描述

5.7 加入 webapp 目录下文件

在这里插入图片描述

6. 在 HelloWorld 基础上加入SpringSecurity

6.1 加入 SpringSecurity 依赖

<!-- SpringSecurityWeb 应用进行权限管理 -->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>4.2.10.RELEASE</version>
        </dependency>
        <!-- SpringSecurity 配置 -->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>4.2.10.RELEASE</version>
        </dependency>
        <!-- SpringSecurity 标签库 -->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-taglibs</artifactId>
            <version>4.2.10.RELEASE</version>
        </dependency>

6.2 加入 SpringSecurity 控制权限的 Filter

SpringSecurity 使用的是过滤器 Filter而不是拦截器 Interceptor,意味着 SpringSecurity能够管理的不仅仅是 SpringMVC 中的 handler 请求,还包含 Web 应用中所有请求。比如:项目中的静态资源也会被拦截,从而进行权限控制。

<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>

特 别 注 意 : springSecurityFilterChain 标 签 中 必 须 是
springSecurityFilterChain。因为 springSecurityFilterChain 在 IOC 容器中对应真正执行权限控制的二十几个 Filter,只有叫这个名字才能够加载到这些 Filter。

6.3 加入配置类

com.atguigu.security.config.WebAppSecurityConfig

@Configuration
@EnableWebSecurity
public class WebAppSecurityConfig extends WebSecurityConfigurerAdapter {
}

Enable 理解为启用。
@EnableWebSecurity 注解表示启用 Web 安全功能。
以后会接触到很多@EnableXxx 注解,用来启用对应的功能。

6.4 效果

所有请求都被 SpringSecurity 拦截,要求登录才可以访问。
静态资源也都被拦截,要求登录。
登录失败有错误提示。

7. SpringSecurity 操作实验

下面的操作都是在 HelloWorld 的基础上逐步增加权限控制设置,循序渐进学习
SpringSecurity 用法。

7.1 实验 1:放行首页和静态资源

在配置类中重写父类的 configure(HttpSecurity security)方法。

protected void configure(HttpSecurity security) throws Exception {
logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially
override subclass configure(HttpSecurity).");
security
.authorizeRequests()
.anyRequest().authenticated()
//所有请求都需要进行认证
.and()
.formLogin()
.and()
.httpBasic();
}

重写后

@Override
protected void configure(HttpSecurity security) throws Exception {
//super.configure(security); 注释掉将取消父类方法中的默认规则
security.authorizeRequests()
//对请求进行授权
.antMatchers("/layui/**","/index.jsp")
//使用 ANT 风格设置要授权的 URL 地.permitAll()
//允许上面使用 ANT 风格设置的全部请求
.anyRequest()
//其他未设置的全部请求
.authenticated();
//需要认证
}

效果:未认证的请求会跳转到 403 错误页面。
在这里插入图片描述

7.2 实验 2:未认证请求跳转到登录页

@Override
protected void configure(HttpSecurity security) throws Exception {
//super.configure(security); 注释掉将取消父类方法中的默认规则
security.authorizeRequests()
//对请求进行授权
.antMatchers("/layui/**","/index.jsp")
//使用 ANT 风格设置要授权的 URL 地.permitAll()
//允许上面使用 ANT 风格设置的全部请求
.anyRequest()
//其他未设置的全部请求
.authenticated()
//需要认证
.and()
.formLogin()
//设置未授权请求跳转到登录页面
.loginPage("/index.jsp")
//指定登录页
.permitAll();
//为登录页设置所有人都可以访问
}

指定登录页前后 SpringSecurity 登录地址变化:
指定前
/login GET - the login form
/login POST - process the credentials and if valid authenticate the user
/login?error GET - redirect here for failed authentication attempts
/login?logout GET - redirect here after successfully logging out
指定后
/index.jsp GET - the login form
/index.jsp POST - process the credentials and if valid authenticate the user
/index.jsp?error GET - redirect here for failed authentication attempts
/index.jsp?logout GET - redirect here after successfully logging out
在这里插入图片描述
通过调用 loginProcessingUrl()方法指定登录地址。

security
.authorizeRequests()
// 对请求进行授权
……
// loginProcessingUrl()方法指定了登录地址,就会覆盖 loginPage()方法中设置的默认值
/index.jsp POST
.loginProcessingUrl("/do/login.html")
// 指定提交登录表单的地址
;

7.3 实验 3:设置登录系统的账号、密码

在这里插入图片描述

7.3.1 页面设置

给 index.jsp 设置表单

在这里插入图片描述
注意:要取消页面的“假”提交。不用管 layui 的语法。
在这里插入图片描述
账号、密码的请求参数名

SpringSecurity 默认账号的请求参数名:username
SpringSecurity 默认密码的请求参数名:password

要么修改页面上的表单项的 name 属性值,要么修改配置。如果修改配置可以调用
usernameParameter()和 passwordParameter()方法。
在这里插入图片描述

@Override
    protected void configure(HttpSecurity security) throws Exception {
    .usernameParameter("loginAcct") // 定制登录账号的请求参数名
                .passwordParameter("userPswd") // 定制登录密码的请求参数名
                
      }

7.3.2 后端配置

设置登录成功后默认前往的页面

@Override
    protected void configure(HttpSecurity security) throws Exception {
    
    . .authorizeRequests() // 对请求进行授权
                .anyRequest()   // 任意的请求
                .authenticated() // 需要登录以后才可以访问
                .and()
                .formLogin() //使用表单形式登录

                // 关于loginPage()方法的特殊说明
                // 指定登录页的同时会影响到:“提交登录表单的地址”、“退出登录地址”、“登录失败地址”
                // /index.jsp GET - the login form 去登录页面
                // /index.jsp POST - process the credentials and if valid authenticate the user 提交登录表单
                // /index.jsp?error GET - redirect here for failed authentication attempts 登录失败
                // /index.jsp?logout GET - redirect here after successfully logging out 退出登录
                // 指定登录页的同时会影响到:"提交登录表单的地址"、"退出登录地址"、"登录失败地址"
                .loginPage("/index.jsp") // 指定登录页面(如果没有指定会访问SpringSecurity自带的登录页)


                // loginProcessingUrl()方法指定了登录地址,就会覆盖loginPage()方法中设置的默认值/index.jsp POST
                .loginProcessingUrl("/do/login.html") //指定提交登录表单的地址
                .permitAll() // 登录地址本身也需要permitAll()放行

                .usernameParameter("loginAcct") // 定制登录账号的请求参数名
                .passwordParameter("userPswd") // 定制登录密码的请求参数名
                .defaultSuccessUrl("/main.html")		// 登录成功后前往的地址
                /*.and()
                .csrf()
                .disable() // 禁用CSRF功能*/
                .and()          
      }

重写另外一个父类的方法,来设置登录系统的账号密码

@Override
    protected void configure(AuthenticationManagerBuilder builder) throws Exception {
builder
                .inMemoryAuthentication() //在内存中完成账号、密码的检查
                .withUser("tom") // 指定账号
                .password("123123") // 指定密码
                .roles("ADMIN","学徒") // 指定当前用户的角色
                .and()
                .withUser("jerry") // 指定账号
                .password("123123") // 指定密码
                .authorities("UPDATE","内门弟子") // 指定当前用户的权限
        ;
        }

Cannot pass a null GrantedAuthority collection 问 题 是 由 于 没 有 设 置 roles() 或 authorities()方法导致的。
实现的最后效果:登录成功后具体资源都可以访问了。

7.3.3 ※了解:_csrf 如何防止跨站请求伪造?

Cross-site request forgery 跨站请求伪造
发送登录请求时没有携带_csrf 值,则返回下面错误:
在这里插入图片描述
从钓鱼网站的页面提交的请求无法携带正确、被承认的令牌。
在这里插入图片描述

7.4 实验 4:用户注销

通过调用 HttpSecurity 对象的一系列方法设置注销功能。
logout()方法:开启注销功能
logoutUrl()方法:自定义注销功能的 URL 地址
在这里插入图片描述
如果 CSRF 功能没有禁用,那么退出请求必须是 POST 方式。如果禁用了 CSRF
功能则任何请求方式都可以。
logoutSuccessUrl()方法:退出成功后前往的 URL 地址
addLogoutHandler()方法:添加退出处理器
logoutSuccessHandler()方法:退出成功处理器

退出的表单

<form
id="logoutForm"
action="${pageContext.request.contextPath}/my/logout"
method="post">
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
</form>
<script type="text/javascript">
	window.onload = function(){
	var anchor = document.getElementById("logoutAnchor");
	anchor.onclick = function(){
	document.getElementById("logoutForm").submit();
	return false;
};
};
</script>
<a id="logoutAnchor" href="">退出</a>

7.5 实验 5:基于角色或权限进行访问控制

通过 HttpSecurity 对象设置资源的角色要求
在这里插入图片描述
访问被拒绝后看到 403 错误页面:
在这里插入图片描述
注意:调用顺序
在这里插入图片描述
蓝色代码设置范围更大
红色代码设置范围相对小
如果蓝色代码先调用,会把后面红色代码的设置覆盖,导致红色代码无效。所以要
先做具体小范围设置,再做大范围模糊设置。
注意:SpringSecurity 会在角色字符串前面加“ROLE_”前缀
在这里插入图片描述
之所以要强调这个事情,是因为将来从数据库查询得到的用户信息、角色信息、权限信息需要我们自己手动组装。手动组装时需要我们自己给角色字符串前面加“ROLE_”
前缀

7.6 实验 6:自定义 403 错误页面

由 main.jsp 复制得到 no_auth.jsp。修改如下
在这里插入图片描述

<div class="layui-body">
			<!-- 内容主体区域 -->
			<div style="padding: 15px;">
				<h1>非常抱歉!您没有访问这个功能的权限!(回家照照镜子)</h1>
				<h2>${message }</h2>
			</div>

前往自定义页面方式一:
在这里插入图片描述

@RequestMapping("/to/no/auth/page.html")
	public String toNoAuthPage() {
		return "no_auth";
	}

前往自定义页面方式二:
在这里插入图片描述

 @Override
    protected void configure(HttpSecurity security) throws Exception {

               .exceptionHandling() // 指定异常处理器
//                .accessDeniedPage("/to/no/auth/page.html") // 访问被拒绝时前往的页面
                  .accessDeniedHandler(new AccessDeniedHandlerImpl(){

                      @Override
                      public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
                          request.setAttribute("message","抱歉!您无法访问这个资源!***");
                          request.getRequestDispatcher("/WEB-INF/views/no_auth.jsp").forward(request, response);
                      }
                  })
                .and()
                }

7.7 实验 7:记住我-内存版(不重要)

HttpSecurity 对象调用 rememberMe()方法。
登录表单携带名为 remember-me 的请求参数。具体做法是将登录表单中的checkbox 的 name 设置为 remember-me

<input type="checkbox" name="remember-me"
lay-skin="primary"
title="记住密码">

如 果 不 能 使 用 “ remember-me ” 作 为 请 求 参 数 名 称 , 可 以 使 用rememberMeParameter()方法定制。
记住我原理简要分析:
在这里插入图片描述
通过开发者工具看到浏览器端存储了名为 remember-me 的 Cookie。根据这个 Cookie的 value 在服务器端找到以前登录的 User
在这里插入图片描述
而且这个 Cookie 被设置为存储 2 个星期的时间。
在这里插入图片描述

7.8 实验 8:记住我-数据库版(不重要)

为了让服务器重启也不影响记住登录状态,将用户登录状态信息存入数据库。

7.8.1 建立数据库连接

加入依赖

<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.12</version>
        </dependency>
        <!-- mysql 驱动 -->
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>4.3.20.RELEASE</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 DATABASE `security` CHARACTER SET utf8;

在 WebAppSecurityConfig 类中注入数据源
在这里插入图片描述

 @Autowired
    private DataSource dataSource;

7.8.2 启用令牌仓库功能

在这里插入图片描述
在这里插入图片描述

@Override
    protected void configure(HttpSecurity security) throws Exception {
dbcTokenRepositoryImpl tokenRepository = new dbcTokenRepositoryImpl();
        tokenRepository.setDataSource(dataSource);
   }

注意:需要进入 JdbcTokenRepositoryImpl 类中找到创建 persistent_logins 表的 SQL 语句创建
persistent_logins 表

CREATE TABLE persistent_logins (
username VARCHAR (64) NOT NULL,
series VARCHAR (64) PRIMARY KEY,
token VARCHAR (64) NOT NULL,
last_used TIMESTAMP NOT NULL
);

7.9 实验 9:查询数据库完成认证

7.9.1 了解:SpringSecurity 默认实现

builder.jdbcAuthentication().usersByUsernameQuery(“tom”);在usersByUsernameQuery(“tom”)等方法中最终调用 JdbcDaoImpl 类的方法查询
数据库。
在这里插入图片描述
SpringSecurity 的默认实现已经将 SQL 语句硬编码在了 JdbcDaoImpl 类中。这种
情况下,我们有下面三种选择:
按照 JdbcDaoImpl 类中 SQL 语句设计表结构。
修改 JdbcDaoImpl 类的源码。
不使用 jdbcAuthentication()。

7.9.2 自定义数据库查询方式

builder.userDetailsService(userDetailsService)
其中userDetailsService需要自定义实现UserDetailsService接口的类并自动装配
在这里插入图片描述

package com.atguigu.security.config;

import com.atguigu.security.entity.Admin;
import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.core.GrantedAuthority;

import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;


import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Component
public class MyUserDetailsService implements UserDetailsService {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    // 总目标: 根据表单提交的用户名查询User对象,并装配角色、权限等信息
    @Override
    public UserDetails loadUserByUsername(
            // 表单提交的用户名
            String username) throws UsernameNotFoundException {
            // 1.从数据库查询Admin对象
            String sql = "SELECT id,loginacct,userpswd,username,email FROM t_admin WHERE loginacct = ?";

        List<Admin> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Admin.class), username);
        Admin admin = list.get(0);

        // 2.给Admin设置角色权限信息
        List<GrantedAuthority> authorities = new ArrayList<>();

        authorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
        authorities.add(new SimpleGrantedAuthority("UPDATE"));

        // 3.把admin对象和authorities封装到UserDetails中

        String userpswd = admin.getUserpswd();

        return new User(username, userpswd, authorities);
    }
}

7.9.3 使用自定义 UserDetailsService 完成登录

在这里插入图片描述

@Autowired
    private MyUserDetailsService userDetailsService;
    @Override
    protected void configure(AuthenticationManagerBuilder builder) throws Exception {
//builder
//.inMemoryAuthentication()
//.withUser("tom")
// 指定登录系统的账号
//.password("123123")
// 指定账号对应的密码
//
.roles("大师");
// 必须设置角色或权限,否则会出现 Cannot pass a null
GrantedAuthority collection 错误
builder.userDetailsService(userDetailService);
}

7.9.4 “ROLE_”前缀问题

在这里插入图片描述
org.springframework.security.core.authority.AuthorityUtils.createAuthorityList(String…)工具方法获取创建 SimpleGrantedAuthority 对象添加角色时需要手动在角色名称前
加“ROLE_”前缀

7.10实验 10:应用自定义密码加密规则

自定义类实现 org.springframework.security.crypto.password.PasswordEncoder(使用没有过时的)接口。
在这里插入图片描述

package com.atguigu.security.config;

import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Objects;

@Component
public class MyPasswordEncoder implements PasswordEncoder {
    @Override
    public String encode(CharSequence rawPassword) {

        return privateEncode(rawPassword);

    }

    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {

        // 1.明文密码进行加密
        String formPassword = privateEncode(rawPassword);

        // 2.声明数据库密码
        String databasePassword = encodedPassword;

        // 3.比较
        return Objects.equals(formPassword, databasePassword);
    }

    private String privateEncode(CharSequence rawPassword){
        // 1.创建MessageDigest对象
        try {
            // 1.创建MseeageDigest对象
            String algorithm = "MD5";
            MessageDigest messageDigest = MessageDigest.getInstance("MD5");

            // 2.获取rawPassword的字节数组
            byte[] input = ((String)rawPassword).getBytes();

            // 3.加密
            byte[] output = messageDigest.digest(input);

            // 4.转换为16进制数对应的字符
            String encoded = new BigInteger(1, output).toString(16).toUpperCase();

            return encoded;

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return null;
        }
    }
}

encode()方法对明文进行加密。
matches()方法对明文加密后和密文进行比较。
在配置类中的 configure(AuthenticationManagerBuilder)方法中应用自定义密码加密规则
在这里插入图片描述

@Autowired
   private BCryptPasswordEncoder passwordEncoder;

   // 每次调用这个方法时会检查IOC容器中是否有了对应的bean,如果有就不会真正执行这个函数,因为bean默认是单例的
   //可以使用@Scope(value = "")注解控制是否单例
   @Bean
   public BCryptPasswordEncoder getBCryptPasswordEncoder(){
       return new BCryptPasswordEncoder();
   }
   @Override
    protected void configure(AuthenticationManagerBuilder builder) throws Exception {
    // 装配userDetailsServiced对象
            builder
                    .userDetailsService(userDetailsService).passwordEncoder(getBCryptPasswordEncoder());
    }

SpringSecurity 提供的 BCryptPasswordEncoder 加密规则。
BCryptPasswordEncoder 创 建 对 象 后 代 替 自 定 义 passwordEncoder 对 象 即 可 。BCryptPasswordEncoder 在加密时通过加入随机盐值让每一次的加密结果都不同。能够
避免密码的明文被猜到。
而在对明文和密文进行比较时,BCryptPasswordEncoder 会在密文的固定位置取出盐值,重新进行加密。
在这里插入图片描述
测试代码

package com.atguigu.spring.security;

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

public class SecurityTest {
    public static void main(String[] args) {

        // 1.创建BCryptPasswordEncoder对象
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

        // 2.准备明文字符串
        String rawPassword = "123123";

        // 3.加密
        String encode = passwordEncoder.encode(rawPassword);
        System.out.println(encode);

    }

}
class EncodeTest{
    public static void main(String[] args) {

        // 1.准备明文字符串
        String rawPassword = "123123";

        // 2.准备密文字符串
        String encodedPassword = "$2a$10$FfLBRPb3VSHvskcdsuE5gOvnZPZZlUV8vX54y4o7oR8qA7bsHFhEG";

        // 3.创建BCryptPasswordEncoder
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

        // 3.比较
        boolean matcheResult = passwordEncoder.matches(rawPassword, encodedPassword);

        System.out.println(matcheResult ? "一致" : "不一致");
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值