全网最详细 Spring Security 使用说明

项目开发过程中,几乎每一次的项目交付都会遇到系统认证与安全问题。而之前的方法都是“兵来将挡水来土掩”,没有一套完善的开发流程与规范,受尽折磨。所以这次借助开发新项目的机会,尝试了 Spring Security 框架来进行安全模块的开发。

pom.xml



	org.springframework.security
	spring-security-core
	4.2.3.RELEASE


	org.springframework.security
	spring-security-config
	4.2.3.RELEASE


	org.springframework.security
	spring-security-taglibs
	4.2.3.RELEASE


	org.springframework.security
	spring-security-web
	4.2.3.RELEASE

  

想要使用 Spring Security 安全框架,首先需要引入上述相关 jar 包。

web.xml

    
    
        springSecurityFilterChain
        org.springframework.web.filter.DelegatingFilterProxy
    
    
        springSecurityFilterChain
        /*
    

web.xml 文件中添加 Spring Security 过滤器。

spring-security.xml



    
        
        
        
        
        
        
        
        
        
        
        
        
        
    

	
    
    
	

	
    
    
        
        
            
            
        
    

    
    
    
    
	

    
    

上面的配置文件默认使用的并不是基础配置,因为在实际使用中我们也不会使用基础配置。详细解析如下:

1、http 标签内定义了验证方式和资源拦截规则等,这是不管是使用 xml 配置用户信息还是通过 Java 代码从数据动态获取用户信息都会用的配置。
2、标记为 1 的部分默认是不起作用的,因为用户名、密码、权限都是通过明文写死在配置文件中的,所以事实应用价值不大。但在某些特殊情况下也有其使用价值。如果想使用把标记为 2 的部分注释掉,并把 1 部分打开即可。
3、标记为 2 的部分是本示例中默认的配置。该配置方式定义用户信息认证是通过 Java 代码动态读取数据库的方式获取用户信息。同时定义了用于对比加密后的用户密码的类。
4、文件提供了两种方式的登录事件处理器,一个为默认的登录成功后进行页面跳转,另一个为登录失败后返回 JSON 格式数据信息,这两种方式的样例实现,可以很好的满足日常工作中的开发需求。

spring-mvc.xml

    
    
        
        
    

UserLoginDTO.java

import java.io.Serializable;

/**
 * @ClassName UserLoginDTO
 * @Description  用户登录认证模块 DTO
 * @Author Supreme_Sir
 * @Date 2019/7/10 9:48
 * @Version 1.0
 **/
public class UserLoginDTO implements Serializable {
    private String userID;

    private String userName;

    private String realName;

    private String password;

    private String createTime;

    private String lastLogTime;

    private Integer avaliable;

    private Integer overdue;

    private Integer locked;

    private Integer licence;

    private String privilegeID;

    private String privilegeName;

    private String privilegeTag;

    private static final long serialVersionUID = 1L;
}

属性太多, set、get、toString 就不贴出来了~!

UserLoginDTOMapper.java

import com.supreme.dto.UserLoginDTO;

import java.util.List;

/**
 * @ClassName UserLoginDTOMapper
 * @Description
 * @Author Supreme_Sir
 * @Date 2019/7/10 10:00
 * @Version 1.0
 **/
public interface UserLoginDTOMapper {

    //  根据用户名查询用户权限
    public List selectByUserName(String userName);
}

UserLoginDTOMapper.xml




    
        
        
        
        
        
        
        
        
        
        
        
        
        
    

    

CommonUtil.java

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import com.supreme.dto.UserLoginDTO;

public class CommonUtil {
    /**
     * @Author  Supreme_Sir
     * @Description  将查询到的用户登录对象转换成自定义 Spring Security 用户验证所需格式
     * @Date  2019/7/10 15:59
     * @Param  [list]
     * @return  java.util.Map
     **/
    public static Map convertUserLoginDTO2Map(List list) {
        Map result = new HashMap();
        for (UserLoginDTO dto : list) {
            if (result.size() < 1) {
                result.put("userName", dto.getUserID());
                result.put("password", dto.getPassword());
                /*result.put("userID", dto.getUserID());
                result.put("realName", dto.getUserID());
                result.put("createTime", dto.getUserID());
                result.put("lastLogTime", dto.getUserID());*/
                result.put("avaliable", (dto.getAvaliable()>0)?true:false);
                result.put("overdue", (dto.getOverdue()>0)?true:false);
                result.put("locked", (dto.getLocked()>0)?true:false);
                result.put("licence", (dto.getLicence()>0)?true:false);
                List authorities = new ArrayList();
                authorities.add(new SimpleGrantedAuthority(dto.getPrivilegeTag()));
                result.put("privilege", authorities);
            } else {
                ((List) result.get("privilege")).add(new SimpleGrantedAuthority(dto.getPrivilegeTag()));
            }
        }
        return result;
    }	

    /**
     * @Author  Supreme_Sir
     * @Description  生成 Spring Security 用户密码,新增用户时必须使用该方法对密码进行加密存储
     * @Date  2019/7/10 16:03
     * @Param  [originalPassword]
     * @return  java.lang.String
     **/
    public String passwordGenertor(String originalPassword){
        PasswordEncoder encoder = new BCryptPasswordEncoder();
        return encoder.encode(originalPassword);
    }															
}

由于在密码加密时“加盐”的次数不同,所以即使是相同的密码在调用 passwordGenertor() 方法后,生成的密码也是不同的。但这并不影响用户密码验证。

MyAuthenticationFailureHandler.java

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * @ClassName MyAuthenticationFailureHandler
 * @Description  自定义认证失败处理器,主要用以示范登录失败返回 JSON 数据功能。
 * 如果想自定义登录成功处理器,则实现 AuthenticationSuccessHandler 接口即可
 * @Author Supreme_Sir
 * @Date 2019/7/10 16:09
 * @Version 1.0
 **/
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
    private ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
		AuthenticationException exception) throws IOException, ServletException {
        Map result = new HashMap();
        result.put("result","登录失败");
        response.setContentType("text/json;charset=utf-8");
        response.getWriter().print(objectMapper.writeValueAsString(result));
    }
}

MyUserDetailService.java

import com.supreme.dao.UserLoginDTOMapper;
import com.supreme.utils.CommonUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
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 java.util.ArrayList;
import java.util.Map;

/**
 * @ClassName MyUserDetailService
 * @Description  自定义用户登录
 * @Author Supreme_Sir
 * @Date 2019/7/10 16:09
 * @Version 1.0
 **/
public class MyUserDetailService implements UserDetailsService {

    @Autowired
    private UserLoginDTOMapper mapper;

    /**
     * @Author  Supreme_Sir
     * @Description  根据用户名动态读取用户信息并认证
     * @Date  2019/7/10 17:03
     * @Param  [username]
     * @return  org.springframework.security.core.userdetails.UserDetails
     **/
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Map map = CommonUtil.convertUserLoginDTO2Map(mapper.selectByUserName(username));
        User user = new User((String) map.get("userName"), (String) map.get("password"), (boolean) map.get("avaliable"),
                (boolean) map.get("overdue"), (boolean) map.get("licence"), (boolean) map.get("locked"),
                (ArrayList) map.get("privilege"));
        return user;
    }
}

authority.sql

/*
 Navicat Premium Data Transfer

 Source Server         : locationMySQL
 Source Server Type    : MySQL
 Source Server Version : 50560
 Source Host           : localhost:3306
 Source Schema         : authority

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

 Date: 11/07/2019 11:38:12
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for privilege
-- ----------------------------
DROP TABLE IF EXISTS `privilege`;
CREATE TABLE `privilege`  (
  `privilege_id` varchar(50) CHARACTER SET gbk COLLATE gbk_chinese_ci NOT NULL COMMENT '权限ID',
  `privilege_name` varchar(50) CHARACTER SET gbk COLLATE gbk_chinese_ci NULL DEFAULT NULL COMMENT '权限名',
  `privilege_tag` varchar(50) CHARACTER SET gbk COLLATE gbk_chinese_ci NULL DEFAULT NULL COMMENT '权限标识符',
  PRIMARY KEY (`privilege_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = gbk COLLATE = gbk_chinese_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Records of privilege
-- ----------------------------
INSERT INTO `privilege` VALUES ('1', '访问用户内容', 'ROLE_USER');
INSERT INTO `privilege` VALUES ('2', '访问管理员内容', 'ROLE_ADMIN');

-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role`  (
  `role_id` varchar(50) CHARACTER SET gbk COLLATE gbk_chinese_ci NOT NULL DEFAULT '' COMMENT '角色ID',
  `role_name` varchar(50) CHARACTER SET gbk COLLATE gbk_chinese_ci NULL DEFAULT NULL COMMENT '角色名',
  `role_description` varchar(100) CHARACTER SET gbk COLLATE gbk_chinese_ci NULL DEFAULT NULL COMMENT '角色说明',
  PRIMARY KEY (`role_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = gbk COLLATE = gbk_chinese_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES ('1', 'user', '普通用户');
INSERT INTO `role` VALUES ('2', 'admin', '管理员');

-- ----------------------------
-- Table structure for role_privilege
-- ----------------------------
DROP TABLE IF EXISTS `role_privilege`;
CREATE TABLE `role_privilege`  (
  `role_privilege_roleid` varchar(50) CHARACTER SET gbk COLLATE gbk_chinese_ci NOT NULL COMMENT '角色ID',
  `role_privilege_privilegeid` varchar(50) CHARACTER SET gbk COLLATE gbk_chinese_ci NOT NULL COMMENT '权限ID'
) ENGINE = InnoDB CHARACTER SET = gbk COLLATE = gbk_chinese_ci ROW_FORMAT = Compact;

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

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `user_id` varchar(50) CHARACTER SET gbk COLLATE gbk_chinese_ci NOT NULL COMMENT '主键',
  `user_name` varchar(30) CHARACTER SET gbk COLLATE gbk_chinese_ci NULL DEFAULT NULL COMMENT '用户名',
  `user_realname` varchar(30) CHARACTER SET gbk COLLATE gbk_chinese_ci NULL DEFAULT NULL COMMENT '真实姓名',
  `user_password` varchar(255) CHARACTER SET gbk COLLATE gbk_chinese_ci NULL DEFAULT NULL COMMENT '密码',
  `user_createtime` datetime NULL DEFAULT NULL COMMENT '创建日期',
  `user_lastlogtime` datetime NULL DEFAULT NULL COMMENT '最后登录时间',
  `user_avaliable` int(1) NULL DEFAULT NULL COMMENT '是否可用',
  `user_overdue` int(1) NULL DEFAULT NULL COMMENT '是否过期',
  `user_locked` int(1) NULL DEFAULT NULL COMMENT '是否锁定',
  `user_licence` int(1) NULL DEFAULT NULL COMMENT '证书是否过期',
  PRIMARY KEY (`user_id`) USING BTREE,
  UNIQUE INDEX `用户名`(`user_name`) USING BTREE COMMENT '用户名唯一'
) ENGINE = InnoDB CHARACTER SET = gbk COLLATE = gbk_chinese_ci COMMENT = '用户表' ROW_FORMAT = Compact;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'tom', '汤姆', '$2a$10$byaRxiw/DEyig7y0pniDW.D5OPdJnJXwDnB3f/NcNPq2MGTk1wPom', '2019-07-04 12:00:00', '2019-07-04 00:00:00', 1, 1, 1, 1);
INSERT INTO `user` VALUES ('2', 'jack', '杰克', '$2a$10$p47hUT5n0tfPesWo/uN88.tFXwuTH85KBLnGiaq.NkeRDvy3Qg90y', '2019-07-04 12:00:00', '2019-07-04 00:00:00', 1, 1, 1, 1);

-- ----------------------------
-- Table structure for user_role
-- ----------------------------
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role`  (
  `user_role_userid` varchar(50) CHARACTER SET gbk COLLATE gbk_chinese_ci NOT NULL COMMENT '用户ID',
  `user_role_roleid` varchar(50) CHARACTER SET gbk COLLATE gbk_chinese_ci NOT NULL COMMENT '角色ID'
) ENGINE = InnoDB CHARACTER SET = gbk COLLATE = gbk_chinese_ci ROW_FORMAT = Compact;

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

-- ----------------------------
-- Triggers structure for table user
-- ----------------------------
DROP TRIGGER IF EXISTS `user_delete_trigger`;
delimiter ;;
CREATE TRIGGER `user_delete_trigger` AFTER DELETE ON `user` FOR EACH ROW BEGIN 
	DELETE FROM user_role WHERE user_role_userid=old.user_id;
END
;;
delimiter ;

SET FOREIGN_KEY_CHECKS = 1;

别忘了要先创建 authority 数据库,字符集为 gbk

login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>


    自定义登录页


    

THIS IS MYSELF LOGIN PAGE



index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>


Hello World!

User的专属页面
Admin的专属页面
文件操作页面
登出

admin.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>


    Admin


    

THIS IS ADMIN PAGE

user.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>


    User


    

THIS IS USER PAGE

filePage.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>


    文件操作页


    



下载 测试.txt

你可能感兴趣的:(后台)