Spring Security:userDetailsService用户认证以及授权

编写:Horin


前言

Spring 安全性是提供认证、授权和防范常见攻击的框架。由于对imperative和reactive应用程序的安全都提供了第一类支持,因此它是保护基于 Spring 的应用程序的事实上的标准。

有关功能的完整列表,请参见引用的Features部分。
Spring安全 中文文档


提示:以下是本篇文章正文内容,下面案例可供参考

一、导入框架依赖

1、添加依赖:pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.7</version>
        <relativePath/> <!-- lookup parent from repository -->

    </parent>

    <groupId>com.example</groupId>
    <artifactId>TheToken</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>TheToken</name>
    <description>TheToken</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!--      Spring boot 热部署 导入一个依赖即可-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- mybatis-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <!-- mysql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- thymeleaf模板代码 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <!-- 安全管理 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <!-- Thymeleaf 整合 Spring Security 组件-->
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity5</artifactId>
        </dependency>
        
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.6.7</version>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

二、操作流程

1.继承WebSecurityConfigurerAdapter并重写方法

配置类:SecurityConfig.java

package com.example.thetoken.config;

import com.example.thetoken.service.serviceImpl.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.access.AccessDeniedException;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import java.io.IOException;


@EnableWebSecurity  // 开启MVC security安全支持
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private DataSource dataSource;
    
    /**
     * userDetailsService身份验证
     */
    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    /**
     * 用户授权管理自定义配置
     * @param http
     * @throws Exception
     */

    /**
     * 用户身份认证自定义配置
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    
        /**
         * 明文加密
         */
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        
        /**
         * 1、内存认证
         */
//        auth.inMemoryAuthentication().passwordEncoder(encoder)
//                .withUser("jsor").password(encoder.encode("12312333")).roles("common")
//                .and()
//                .withUser("wanglaoji").password(encoder.encode("123456")).roles("admin");

        /**
         * 2、UserDetailsService认证
         */
        auth.userDetailsService(userDetailsService).passwordEncoder(encoder);
    }

}

2.实现UserDetailsService接口

实现类:UserDetailsServiceImpl.java

package com.example.thetoken.service.serviceImpl;

import com.example.thetoken.domain.User;
import com.example.thetoken.domain.Authority;
import com.example.thetoken.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.stream.Collectors;

/**
 * @Classname UserDetailsServiceImpl
 * @Description 自定义一个UserDetailsService接口实现类进行用户认证信息封装
 */
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    @Autowired
    private UserMapper userMapper;
    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        /**
         * 查询用户信息
         * selectUser():通过用户名查询用户密码
         * selectAuths():查询该用户权限
         */
        User user = userMapper.selectUser(s);
        List<Authority> authorities = userMapper.selectAuths(s);

        /**
         * 封装用户权限
         */
        List<SimpleGrantedAuthority> list = authorities.stream().map(
                authority -> new SimpleGrantedAuthority(authority.getAuthority())).collect(Collectors.toList());

        /**
         * 将对应值传入security的User对象
         */
        if(user!=null){
            System.out.println(user.getUserName()+"  "+user.getUserAccount()+"  "+user.getUserPassword());
            String userA = user.getUserAccount();
            String pasA = user.getUserPassword();
            System.out.println(list);
            //数据库为明文密码,所以使用对象来转换为需要的加密格式
            BCryptPasswordEncoder bCryptPasswordEncoder=new BCryptPasswordEncoder();
            UserDetails userDetails=
                    new org.springframework.security.core.userdetails.User(userA, bCryptPasswordEncoder.encode(pasA), list);
            System.out.println(userDetails);
            return userDetails;
        } else {

            /**
             * 不存在用户抛出为空
             */
            return null;
        }
    }
}

3.编写数据持久化接口

实现类:UserMapper.java

package com.example.thetoken.mapper;


import com.example.thetoken.domain.Authority;
import com.example.thetoken.domain.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;

import java.util.List;


@Mapper
public interface UserMapper {
	
	//验证用户账号是否匹配
    User selectUser(String userAccount);
    
    //获取该用户名的权限
    List<Authority> selectAuths(String userAccount);

}

4.创建数据库

SQL导出文件:springbootf.sql

/*
 Navicat MySQL Data Transfer

 Source Server         : 127.0.0.1
 Source Server Type    : MySQL
 Source Server Version : 50723
 Source Host           : localhost:3306
 Source Schema         : springbootf

 Target Server Type    : MySQL
 Target Server Version : 50723
 File Encoding         : 65001

 Date: 01/06/2022 19:19:38
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for authority
-- ----------------------------
DROP TABLE IF EXISTS `authority`;
CREATE TABLE `authority`  (
  `id` int(50) NOT NULL,
  `authority` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of authority
-- ----------------------------
INSERT INTO `authority` VALUES (1, 'ROLE_admin');
INSERT INTO `authority` VALUES (2, 'ROLE_common');

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `user_id` int(5) NOT NULL AUTO_INCREMENT,
  `user_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `user_account` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `user_password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `sex` int(1) NOT NULL COMMENT '0-女,1-男',
  PRIMARY KEY (`user_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, '江治雨', '8823', 'j12312333', 1);
INSERT INTO `user` VALUES (2, '周杰伦', '123', '123', 1);
INSERT INTO `user` VALUES (4, '邓丽君', 'dengli312884', '987654321', 0);
INSERT INTO `user` VALUES (6, '张国荣', '401', 'zgr', 1);
INSERT INTO `user` VALUES (7, 's', 's', '', 1);

-- ----------------------------
-- Table structure for user_authority
-- ----------------------------
DROP TABLE IF EXISTS `user_authority`;
CREATE TABLE `user_authority`  (
  `id` int(20) NOT NULL,
  `user_id` int(50) NOT NULL,
  `authority_id` int(50) NOT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user_authority
-- ----------------------------
INSERT INTO `user_authority` VALUES (1, 1, 1);
INSERT INTO `user_authority` VALUES (2, 2, 2);

SET FOREIGN_KEY_CHECKS = 1;


5.Mapper映射文件

映射文件:UserMapper.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.example.thetoken.mapper.UserMapper">

    <resultMap id="BaseResultMap" type="com.example.thetoken.domain.User">
        <id property="userId" column="user_id"/>
        <result property="userName" column="user_name"/>
        <result property="userAccount" column="user_account"/>
        <result property="userPassword" column="user_password"/>
        <result property="sex" column="sex"/>
    </resultMap>
    
    <select id="selectUser" parameterType="string" resultMap="BaseResultMap">
        select * from user where user_account = #{userAccount}
    </select>

    <select id="selectAuths" parameterType="string" resultType="Authority">
        select a.* from user c, authority a, user_authority ca
                   where
                       ca.user_id=c.user_id and ca.authority_id=a.id and c.user_account =#{userAccount}
    </select>
    
</mapper>


6.编写实体类

省略:User实体类、Authority实体类


7.完善SecurityConfig配置:授权、自定义页面等

配置类:SecurityConfig.java

package com.example.thetoken.config;

import com.example.thetoken.service.serviceImpl.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.access.AccessDeniedException;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import java.io.IOException;


@EnableWebSecurity  // 开启MVC security安全支持
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private DataSource dataSource;
    /**
     * 第三种身份验证
     */
    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    /**
     * 用户授权管理自定义配置
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
//        super.configure(http);
        /**
         * 自定义授权
         */
        http.authorizeRequests()
                // 静态资源、登录、注册页面放行
               .antMatchers("/login/**","/moban/**","/toLogin","/toZhuce").permitAll()
//                .antMatchers("/templates/**").hasRole("common")
                //管理员可以访问用户列表(接口list)
                .antMatchers("/list").hasRole("admin")
                .anyRequest().authenticated();

        /**
         * 自定义登录
         */
        http.formLogin()
                //跳转登陆页面接口
                .loginPage("/toLogin")
                //以下Controller接口无需编写
                .loginProcessingUrl("/loginss")
                //主页面接口
                .defaultSuccessUrl("/");

        /**
         * 记住我
         */
        http.rememberMe()
                .rememberMeParameter("rememberme")
                .tokenValiditySeconds(2000);
                // 对cookie信息进行持久化管理
               // .tokenRepository(tokenRepository());

        /**
         * 自定义无权限跳转页面
         */
        http.exceptionHandling().accessDeniedHandler(new AccessDeniedHandler() {
            @Override
            public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
                // 如果是权限访问异常,则进行拦截到指定错误页面
                RequestDispatcher dispatcher = httpServletRequest.getRequestDispatcher("/err");
                dispatcher.forward(httpServletRequest, httpServletResponse);
            }
        });
    }
    /**
     * 持久化Token存储
     * @return
     */
    @Bean
    public JdbcTokenRepositoryImpl tokenRepository(){
        JdbcTokenRepositoryImpl jr=new JdbcTokenRepositoryImpl();
        jr.setDataSource(dataSource);
        jr.setCreateTableOnStartup(false);
        return jr;
    }

    /**
     * 用户身份认证自定义配置
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        /**
         * 明文加密
         */
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        /**
         * 1、内存认证
         */
//        auth.inMemoryAuthentication().passwordEncoder(encoder)
//                .withUser("jsor").password(encoder.encode("12312333")).roles("common")
//                .and()
//                .withUser("wanglaoji").password(encoder.encode("123456")).roles("admin");

        /**
         * 2、UserDetailsService认证
         */
        auth.userDetailsService(userDetailsService).passwordEncoder(encoder);
    }

}

8.登录页面修改为form表单提交

登录页面:index.html

注意事项:仅供参考结构,未上传css样式文件。
重点一,input的name属性必须固定,账号为username,密码为password。
重点二,form标签的action与配置类中的登录接口一样,spring会自动创建。

<!DOCTYPE html>
<html  xmlns:th="http://www.thymeleaf.org">
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title>欢迎登录</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <!-- style CSS -->
  <link th:href="@{/login/css/style.css}" rel="stylesheet">
  <!-- google fonts -->
  <link th:href="@{https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;500;700&display=swap}" rel="stylesheet">
  <script th:src="@{/login/js/jquery-3.1.1.min.js}"></script>
  <script th:src="@{/login/js/jquery.min.js}"></script>
</head>

<body>
<script th:if="${userList!=null}"> alert("账号或者密码输入错误!");</script>

<div class="main">
  <div class="logo text-center">
    <h1> <a href="index.html">叭叭贰叁™ 进销系统</a></h1>
  </div>

  <div class="content-w3ls text-center" >
      <form th:action="@{/loginss}" th:method="post">
      <!-- 用户名-->
      <div class="wthree-field">
        <input name="username" type="text"  placeholder="用户名" />
      </div>

      <!-- 密码-->
      <div class="wthree-field">
        <input name="password"  type="password" placeholder="密码" />
      </div>

      <div class="wthree-field">
        <button  class="btn"  type="submit" >登录</button>
      </div>

      <div class="login-bottom">
        <a th:href="@{/toZhuce}" class="">没有账户?创建一个。</a>
      </div>

      </form>
  </div>


  <div class="copyright">
    <p>© 2021 Universe Signin Form. Made with music | Designed by <a
            href="" target="_blank">Horin</a></p>
  </div>
</div>

</body>

</html>

总结

其中HTML页面、Controller跳转补充完成,就可以正常运行了。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值