大家好,欢迎来到IT知识分享网。
本篇文章目的在于快速使用Spring Boot与Spring Security搭建一个项目,实现简单的用户注册登录。
项目已完整上传到GitHub传送门
此项目所用知识点:
- Spring Boot
- Spring Security
- MySQL
- Mybatis
- JSP
一、项目框架
1. 项目结构
2. 选择安装依赖
- Spring Boot DevTools
- Spring Web
- Spring Security
- Mybatis Framework
- MySQL Driver
以此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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>springboot-security</artifactId> <version>1.0-SNAPSHOT</version> <!-- 引入springboot --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.1.RELEASE</version> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencies> <!-- spring boot --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- spring security--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- jsp --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <scope>provided</scope> </dependency> <!-- jstl --> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> </dependency> <!-- tomcat --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency> <!--用于编译jsp --> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> <scope>provided</scope> </dependency> <!-- test --> <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.1.2</version> </dependency> <!-- mysql --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> </dependency> </dependencies> <!-- 配置tomcat --> <build> <finalName>security-springboot</finalName> <pluginManagement> <plugins> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> <plugin> <artifactId>maven-resources-plugin</artifactId> <configuration> <encoding>utf-8</encoding> <useDefaultDelimiters>true</useDefaultDelimiters> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> <includes> <include>/*</include> </includes> </resource> <resource> <directory>src/main/java</directory> <includes> <include>/*.xml</include> </includes> </resource> </resources> </configuration> </plugin> </plugins> </pluginManagement> </build> </project>
二、数据库
共5张表
- t_user 用户表
- t_role 角色表
- r_user_role 用户-角色
- r_permission 权限表
- r_role_permission 角色-权限
/* Navicat Premium Data Transfer Source Server : localhost Source Server Type : MySQL Source Server Version : 50610 Source Host : localhost:3306 Source Schema : user_db Target Server Type : MySQL Target Server Version : 50610 File Encoding : 65001 Date: 21/07/2020 21:29:29 */ SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for t_permission -- ---------------------------- DROP TABLE IF EXISTS `t_permission`; CREATE TABLE `t_permission` ( `id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `code` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '权限标识符', `description` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '描述', `url` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '请求地址', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; -- ---------------------------- -- Records of t_permission -- ---------------------------- INSERT INTO `t_permission` VALUES ('1', 'p1', '测试资源1', '/resource/r1'); INSERT INTO `t_permission` VALUES ('2', 'p2', '测试资源2', '/resource/r2'); INSERT INTO `t_permission` VALUES ('3', 'p3', '测试资源3', '/resource/r3'); -- ---------------------------- -- Table structure for t_role -- ---------------------------- DROP TABLE IF EXISTS `t_role`; CREATE TABLE `t_role` ( `id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `role_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `description` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `create_time` datetime(0) NULL DEFAULT NULL, `update_time` datetime(0) NULL DEFAULT NULL, `status` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, PRIMARY KEY (`id`) USING BTREE, UNIQUE INDEX `unique_role_name`(`role_name`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; -- ---------------------------- -- Records of t_role -- ---------------------------- INSERT INTO `t_role` VALUES ('1', '管理员', NULL, NULL, NULL, ''); -- ---------------------------- -- Table structure for t_role_permission -- ---------------------------- DROP TABLE IF EXISTS `t_role_permission`; CREATE TABLE `t_role_permission` ( `role_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `permission_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, PRIMARY KEY (`role_id`, `permission_id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; -- ---------------------------- -- Records of t_role_permission -- ---------------------------- INSERT INTO `t_role_permission` VALUES ('1', '1'); INSERT INTO `t_role_permission` VALUES ('1', '2'); -- ---------------------------- -- Table structure for t_user -- ---------------------------- DROP TABLE IF EXISTS `t_user`; CREATE TABLE `t_user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户id', `username` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `password` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `fullname` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用户姓名', `mobile` varchar(11) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '手机号', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; -- ---------------------------- -- Records of t_user -- ---------------------------- INSERT INTO `t_user` VALUES (1, 'zhangsan', '$2a$10$37vdSYJUVguwXpLDnZfEt.UDC0y6Yk2RCzFuJKfOrWCiTnUFlmj3K', NULL, NULL); -- ---------------------------- -- Table structure for t_user_role -- ---------------------------- DROP TABLE IF EXISTS `t_user_role`; CREATE TABLE `t_user_role` ( `user_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `role_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `create_time` datetime(0) NULL DEFAULT NULL, `creator` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, PRIMARY KEY (`user_id`, `role_id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; -- ---------------------------- -- Records of t_user_role -- ---------------------------- INSERT INTO `t_user_role` VALUES ('1', '1', NULL, NULL); SET FOREIGN_KEY_CHECKS = 1;
三、代码实现
实现思路
- 配置Spring Boot环境。将服务器配置、数据源、MyBatis映射文件、启动文件完成配置。
- 完成基本的jsp页面。用于测试用户注册、登录功能。
- 配置视图控制器。将项目的根路径重定向到login-view,再将login-view转发到真实的login.jsp页面。
- 进行Spring Security配置。主要配置密码编码器和安全拦截器
- 编写dao层和mapper文件。用于连接数据库。
- 编写service层。一方面是实现用户的注册和登录,另一方面是实现spring-security核心接口,其负载着用户特定数据,它被用来在整个框架作为一个用户DAO。
- 编写controller层。用于页面访问,在controller接口中可以设置接口的访问权限,让Spring Security进行安全验证。
1. 配置文件
application.properties
# web server.port=8080 server.servlet.context-path=/ spring.application.name=security-springboot spring.mvc.view.prefix=/WEB-INF/view/ spring.mvc.view.suffix=.jsp # DataSource spring.datasource.url=jdbc:mysql://localhost:3306/user_db spring.datasource.username=root spring.datasource.password=root spring.datasource.driver-class-name=com.mysql.jdbc.Driver # mybatis mybatis.mapper-locations=classpath:mapper/*.xml
SpringBootApp.java
package com.aaa; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; / * springboot启动程序 * @author 淮南King */ @SpringBootApplication @MapperScan("com.aaa.dao") public class SpringBootApp {
public static void main(String[] args) {
SpringApplication.run(SpringBootApp.class,args); } }
2. 编写测试页面
login.jsp
<%@ page contentType="text/html;charset=UTF-8" pageEncoding="utf-8" %>
<html> <head> <title>用户登录</title> </head> <body> <h1>登录</h1> <form action="/login" method="post"> 用户名:<input type="text" name="username"><br> 密码:<input type="password" name="password"><br> <input type="submit" value="登录"> </form> <hr> <p><a href="/register">注册</a></p> </body> </html>
register.jsp
<%@ page contentType="text/html;charset=UTF-8" pageEncoding="utf-8" %>
<html> <head> <title>用户注册</title> </head> <body> <h1>注册</h1> <form action="/user/register" method="post"> 用户名:<input type="text" name="username"><br> 密码:<input type="password" name="password"><br> <input type="submit" value="注册"> </form> <hr> <p><a href="/">登录</a></p> </body> </html>
menu.jsp
<%@ page contentType="text/html;charset=UTF-8" pageEncoding="utf-8" %>
<html> <head> <title>菜单页</title> </head> <body> <h1>菜单页</h1> 欢迎${username}登录 <hr> <h3><a href="/resource/r1" target="_blank">资源一</a></h3> <h3><a href="/resource/r2" target="_blank">资源二</a></h3> <h3><a href="/resource/r3" target="_blank">资源三</a></h3> </body> </html>
3. 配置视图控制器
WebConfig.java
package com.aaa.config; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; / * springmvc配置 * @author 淮南King */ @Configuration public class WebConfig implements WebMvcConfigurer {
/ * 用此方法来添加视图控制器。 * @param registry */ @Override public void addViewControllers(ViewControllerRegistry registry) {
//将根路径重定向到/login-view registry.addViewController("/").setViewName("redirect:/login-view"); //将login-view转到login页面 registry.addViewController("/login-view").setViewName("login"); } }
4. 进行Spring Security配置。
WebSecurityConfig.java
package com.aaa.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; / * spring security配置 * * @author 淮南King */ @Configuration @EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
//密码编码器 @Bean public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(); } //安全拦截机制 @Override protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable() .authorizeRequests() //所有/r/的请求必须认证通过 .antMatchers("/r/").authenticated() //除了/r/,其它的请求可以访问 .anyRequest().permitAll() .and() //允许表单登录 .formLogin() //登录页面路径 .loginPage("/login-view") .loginProcessingUrl("/login") //自定义登录成功的页面地址 .successForwardUrl("/login-success") .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) .and() .logout() //登录退出 .logoutUrl("/logout") .logoutSuccessUrl("/login-view?logout"); } }
5. 编写dao层和mapper文件
UserDao.java
package com.aaa.dao; import com.aaa.entity.UserDto; import java.util.List; / * 用户信息持久层 * @author 淮南King */ public interface UserDao {
/ * 根据账号查询用户信息 * * @param username 用户姓名 * @return 用户信息 */ UserDto getUserByUsername(String username); / * 根据用户id查询用户权限 * * @param userId 用户id * @return 权限列表 */ List<String> findPermissionsByUserId(String userId); / * 添加用户 * * @param userDTO 用户信息 * @return 修改条数 */ int addUser(UserDto userDTO); }
UserMapper.xml
注意:mapper文件放置的位置在application.properties中的mybatis.mapper-locations进行路径设置
<?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.aaa.dao.UserDao"> <select id="getUserByUsername" parameterType="String" resultType="com.aaa.entity.UserDto"> select id,username,password,fullname,mobile from t_user where username = #{username} </select> <select id="findPermissionsByUserId" parameterType="String" resultType="String"> SELECT code FROM t_permission WHERE id IN( SELECT permission_id FROM t_role_permission WHERE role_id IN( SELECT role_id FROM t_user_role WHERE user_id = #{id} )) </select> <insert id="addUser" parameterType="com.aaa.entity.UserDto"> INSERT INTO `user_db`.`t_user`(`username`, `password`) VALUES (#{username},#{password}) </insert> </mapper>
6. 编写service层
UserService.java
package com.aaa.service; import com.aaa.dao.UserDao; import com.aaa.entity.UserDto; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.crypto.factory.PasswordEncoderFactories; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import java.util.List; / * 用户信息持久业务层 * * @author 淮南King * @date 2020-07-21 */ @Service public class UserService {
@Autowired UserDao dao; public UserDto getUserByUsername(String username) {
return dao.getUserByUsername(username); } public List<String> findPermissionsByUserId(String userId) {
return dao.findPermissionsByUserId(userId); } / * 添加用户 * @param userDTO * @return */ public int addUser(UserDto userDTO) {
//获取密码编码器 PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder(); //将用户的密码进行编码 String password = passwordEncoder.encode(userDTO.getPassword()); //将编码后的密码覆盖到用户信息中 userDTO.setPassword(password.substring(8)); //将用户信息持久化到数据库中 return dao.addUser(userDTO); } }
package com.aaa.service; import com.aaa.dao.UserDao; import com.aaa.entity.UserDto; import org.springframework.beans.factory.annotation.Autowired; 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.Service; import java.util.List; / * 实现spring-security核心接口,其负载的用户特定数据。 * * @author 淮南King */ @Service public class SpringDataUserDetailsService implements UserDetailsService {
@Autowired UserDao userDao; // 根据账号查询用户信息 @Override public UserDetails loadUserByUsername(String username) {
//将来连接数据库根据账号查询用户信息 UserDto userDto = userDao.getUserByUsername(username); //当查询此用户不存在时,将抛出用户名未找到异常 if (userDto == null) {
throw new UsernameNotFoundException("No such user found, the user name is: "+username); } //根据用户id查询权限 List<String> permissions = userDao.findPermissionsByUserId(userDto.getId()); //将permissions转为数组 String[] permissionArray = new String[permissions.size()]; permissions.toArray(permissionArray); UserDetails userDetails = User.withUsername(userDto.getUsername()).password(userDto.getPassword()).authorities(permissionArray) .build(); return userDetails; } }
7. 编写controller层
package com.aaa.controller; import com.aaa.entity.UserDto; import com.aaa.service.UserService; import com.aaa.util.SecurityUtil; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.ModelAndView; import javax.annotation.Resource; import java.util.HashMap; import java.util.Map; / * @author 淮南King */ @RestController public class LoginController {
@Resource private UserService service; @RequestMapping(value = "/login-success",produces = {
"text/plain;charset=UTF-8"}) public ModelAndView loginSuccess(){
//获取当前线程的SecurityContext Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); //获取当前线程中用户的名称,将名称传递至页面 Map<String,Object> attributes = new HashMap<>(); attributes.put("username",SecurityUtil.getUserNameByAuthentication(authentication)); return new ModelAndView("menu",attributes); } @PostMapping("/user/register") public String register(UserDto userDto) {
service.addUser(userDto); return userDto.getUsername()+"注册成功"; } @GetMapping("/register") public ModelAndView registerView() {
return new ModelAndView("register"); } }
package com.aaa.controller; import com.aaa.util.SecurityUtil; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; / * @author 淮南King * @date 2020-07-21 */ @RestController @RequestMapping("/resource") public class AuthController {
/ * 测试资源1 * 拥有p1权限才可以访问 * * @return */ @GetMapping(value = "/r1", produces = {
"text/plain;charset=UTF-8"}) @PreAuthorize("hasAuthority('p1')") public String resource1() {
//获取当前线程的SecurityContext Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); //获取当前线程的名称 return SecurityUtil.getUserNameByAuthentication(authentication) + " 访问资源1"; } / * 测试资源2 * 拥有p2权限才可以访问 * * @return */ @GetMapping(value = "/r2", produces = {
"text/plain;charset=UTF-8"}) @PreAuthorize("hasAuthority('p2')") public String resource2() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); return SecurityUtil.getUserNameByAuthentication(authentication) + " 访问资源2"; } / * 测试资源3 * 拥有p3权限才可以访问 * * @return */ @GetMapping(value = "/r3", produces = {
"text/plain;charset=UTF-8"}) @PreAuthorize("hasAuthority('p3')") public String resource3() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); return SecurityUtil.getUserNameByAuthentication(authentication) + " 访问资源3"; } }
附:实体类与帮助类
实体类
UserDto.java
package com.aaa.entity; / * DTO:与数据库保持一致<br> * 用户信息 * @author 淮南King */ public class UserDto {
/ * 用户id */ private String id; / * 用户名 */ private String username; / * 用户密码 */ private String password; / * 用户角色ID */ private Integer roleId; public String getId() {
return id; } public void setId(String id) {
this.id = id; } public String getUsername() {
return username; } public void setUsername(String username) {
this.username = username; } public String getPassword() {
return password; } public void setPassword(String password) {
this.password = password; } public Integer getRoleId() {
return roleId; } public void setRoleId(Integer roleId) {
this.roleId = roleId; } }
PermissionDto.java
package com.aaa.entity; / * 权限信息 * @author 淮南King */ public class PermissionDto {
/ * 权限id */ private String id; / * 权限代号 */ private String code; / * 权限描述 */ private String description; / * 路径 */ private String url; public String getId() {
return id; } public void setId(String id) {
this.id = id; } public String getCode() {
return code; } public void setCode(String code) {
this.code = code; } public String getDescription() {
return description; } public void setDescription(String description) {
this.description = description; } public String getUrl() {
return url; } public void setUrl(String url) {
this.url = url; } }
帮助类
SecurityUtil.java
package com.aaa.util; import org.springframework.security.core.Authentication; import org.springframework.security.core.userdetails.UserDetails; / * Security帮助类 * @author 淮南King * @date 2020-07-21 */ public class SecurityUtil {
/ * 根据当前执行线程的SecurityContext获取用户名称 * @param authentication 当前认证通过的用户身份 * @return 用户名 */ public static String getUserNameByAuthentication(Authentication authentication){
String username = null; //用户身份 Object principal = authentication.getPrincipal(); if(principal == null){
return "匿名"; } if(principal instanceof UserDetails){
UserDetails userDetails = (UserDetails)principal; username = userDetails.getUsername(); }else{
username = principal.toString(); } return username; } }
项目已完整上传到GitHub传送门
end…
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/118047.html

