应用id_Spring Boot 19 Security JDBC应用

19.1本节目标

本节基于以下技术:

  • Spring Boot 2.x
  • Spring Security
  • Spring JDBC
  • Thymeleaf
  • Database: MySQL

在这篇文章中,我将指导你使用Spring Boot + Spring Security + JDBC + Thymeleaf创建一个登录应用程序,并解释Spring Security的工作原理。

5f16ea25a55d13a1d07bec29161dfa2a.png

用户需要登录以后,才可以查看受保护页面。

已经登录系统的用户只允许查看其范围和角色内的页面。如果他们访问超出他们角色的受保护页面,他们将被拒绝。

19.2 准备数据库表

DDL语句:

-- Create tablecreate table LOGIN_USER(  USER_ID           BIGINT not null,  USER_NAME         VARCHAR(36) not null,  ENCRYTED_PASSWORD VARCHAR(128) not null,  ENABLED           BIT not null ) ;--  alter table LOGIN_USER  add constraint LOGIN_USER_PK primary key (USER_ID); alter table LOGIN_USER  add constraint LOGIN_USER_UK unique (USER_NAME);  -- Create tablecreate table LOGIN_ROLE(  ROLE_ID   BIGINT not null,  ROLE_NAME VARCHAR(30) not null) ;--  alter table LOGIN_ROLE  add constraint LOGIN_ROLE_PK primary key (ROLE_ID); alter table LOGIN_ROLE  add constraint LOGIN_ROLE_UK unique (ROLE_NAME);  -- Create tablecreate table USER_ROLE(  ID      BIGINT not null,  USER_ID BIGINT not null,  ROLE_ID BIGINT not null);--  alter table USER_ROLE  add constraint USER_ROLE_PK primary key (ID); alter table USER_ROLE  add constraint USER_ROLE_UK unique (USER_ID, ROLE_ID); alter table USER_ROLE  add constraint USER_ROLE_FK1 foreign key (USER_ID)  references USER_USER (USER_ID); alter table USER_ROLE  add constraint USER_ROLE_FK2 foreign key (ROLE_ID)  references USER_ROLE (ROLE_ID);  -- Used by Spring Remember Me API.  CREATE TABLE Persistent_Logins (     username varchar(64) not null,    series varchar(64) not null,    token varchar(64) not null,    last_used timestamp not null,    PRIMARY KEY (series)     ); -------------------------------------- insert into login_User (USER_ID, USER_NAME, ENCRYTED_PASSWORD, ENABLED)values (2, 'user1', '$2a$10$PrI5Gk9L.tSZiW9FXhTS8O8Mz9E97k2FZbFvGFFaSsiTUIl.TCrFu', 1); insert into login_User (USER_ID, USER_NAME, ENCRYTED_PASSWORD, ENABLED)values (1, 'admin1', '$2a$10$PrI5Gk9L.tSZiW9FXhTS8O8Mz9E97k2FZbFvGFFaSsiTUIl.TCrFu', 1); --- insert into login_role (ROLE_ID, ROLE_NAME)values (1, 'ROLE_ADMIN'); insert into login_role (ROLE_ID, ROLE_NAME)values (2, 'ROLE_USER'); --- insert into user_role (ID, USER_ID, ROLE_ID)values (1, 1, 1); insert into user_role (ID, USER_ID, ROLE_ID)values (2, 1, 2); insert into user_role (ID, USER_ID, ROLE_ID)values (3, 2, 2);
dd4b2b5e8a358a5ee61a19c43580d704.png

19.3 创建Spring Boot应用

在Eclipse上,创建一个Spring Boot项目。

输入:

  • Name: SpringBootLogin
  • Group: me.laocat
  • Artifact: SpringBootLogin
  • Description: Spring Boot + JDBC + Security + Thymeleaf
  • Package: me.laocat.login
e14a3d391594fdf4642318606eb79ddc.png

下一步,选择要使用的技术和库(在本节中,我们将使用MySQL数据库)。

数据库:

  • MySQL

技术:

  • Web
  • Thymeleaf
  • Security
b873b42bdde746865c4b9db2b0a022d9.png

项目创建成功后的目录结构:

52080928dbe8ab89ee1defb37f384ea7.png

SpringBootLoginApplication.java

package me.laocat.login;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class SpringBootLoginApplication {public static void main(String[] args) {SpringApplication.run(SpringBootLoginApplication.class, args);}}

19.4 pom.xml

<?xml version="1.0" encoding="UTF-8"?>4.0.0org.springframework.bootspring-boot-starter-parent2.2.11.RELEASEme.laocatSpringBootLogin1.0.0SpringBootLoginSpring Boot + JDBC + Security + Thymeleaf1.8org.springframework.bootspring-boot-starter-securityorg.springframework.bootspring-boot-starter-thymeleaforg.springframework.bootspring-boot-starter-weborg.thymeleaf.extrasthymeleaf-extras-springsecurity5mysqlmysql-connector-javaruntimeorg.springframework.bootspring-boot-starter-testtestorg.junit.vintagejunit-vintage-engineorg.springframework.securityspring-security-testtestorg.springframework.bootspring-boot-maven-plugin

19.5 配置数据源

为了将Spring连接到数据库,您需要在application.properties文件中配置必要的参数

8f0806484e77414f2ec0e7b4008292a7.png
# ===============================# DATABASE# ===============================spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driverspring.datasource.url=jdbc:mysql://localhost:3306/springboot?serverTimezone=UTCspring.datasource.username=rootspring.datasource.password=root

19.6 Spring Security配置

535d7af276cfe401fb4c5a3a7dfcbc09.png

本应用程序有一些功能(页面),例如以下部分:

  • /userInfo

这是用于查看用户信息的页面。此页面要求用户登录并具有admin或user之类的角色权限。

  • /admin

这是一个管理员页面。它需要用户登录,并且只有具有admin角色的人才可以访问。

  • /. /welcome, /login, /logout, /403

该应用程序的所有其他页面均不需要用户登录。

WebSecurityConfig 类用来为应用程序配置的安全性。它由@Configuration注释 这个注释告诉 Spring 这是一个配置类,因此,它将在应用程序运行时由Sping解析 。

@EnableWebSecurity表示启用Spring Security功能。

WebSecurityConfig.java

package me.laocat.login.config;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;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.crypto.bcrypt.BCryptPasswordEncoder;@Configuration@EnableWebSecuritypublic class WebSecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate UserDetailsServiceImpl userDetailsService;@Beanpublic BCryptPasswordEncoder passwordEncoder() {BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();return bCryptPasswordEncoder;}@Autowiredpublic void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {// 设置在数据库中查找用户的服务和PassswordEncoderauth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable();// 这部分页面不需要登陆http.authorizeRequests().antMatchers("/", "/login", "/logout").permitAll();// userInfo页面需要具备 普通用户 或 管理员角色 登录。// 如果没有登录,它将重定向到/登录页面。http.authorizeRequests().antMatchers("/userInfo").access("hasAnyRole('ROLE_USER', 'ROLE_ADMIN')");// /amdin请求,只能是 管理员 角色http.authorizeRequests().antMatchers("/admin").access("hasRole('ROLE_ADMIN')");// 当用户以 普通用户 角色登陆时.// 但是访问需要角色 管理员 的页面// 将抛出AccessDeniedExceptionhttp.authorizeRequests().and().exceptionHandling().accessDeniedPage("/403");// 登陆表单配置http.authorizeRequests().and().formLogin() 提交登陆url地址.loginProcessingUrl("/j_spring_security_check") // 提交 URL.loginPage("/login").defaultSuccessUrl("/userAccountInfo") // 登陆成功后   默认会请求url  .failureUrl("/login?error=true")  // 登陆失败以后  默认会请求的url.usernameParameter("username").passwordParameter("password")// 配置登出页面.and().logout().logoutUrl("/logout").logoutSuccessUrl("/logoutSuccessful");}}

19.7 Model, Mapper, DAO, WebUtils

User类表示数据库LOGIN_USER表中的一条记录。

User.java

package me.laocat.login.model;/** * 描述: 用户类 * @author laocat */public class User {private Long userId; // 主键     private String userName; // 用户名     private String encrytedPassword; // 加密后的密码    public User(Long userId, String userName, String encrytedPassword) {super();this.userId = userId;this.userName = userName;this.encrytedPassword = encrytedPassword;}public Long getUserId() {return userId;}public void setUserId(Long userId) {this.userId = userId;}public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}public String getEncrytedPassword() {return encrytedPassword;}public void setEncrytedPassword(String encrytedPassword) {this.encrytedPassword = encrytedPassword;}@Overridepublic String toString() {return "User [userId=" + userId + ", userName=" + userName + ", encrytedPassword=" + encrytedPassword + "]";}}

UserMapper类用于将LOGIN_USER表中的字段与User类中的属性进行映射(基于查询语句)。

UserMapper.java

package me.laocat.login.mapper;import java.sql.ResultSet;import java.sql.SQLException;import org.springframework.jdbc.core.RowMapper;import me.laocat.login.model.User;/** * 描述:用户Mapper类  * @author laocat */public class UserMapper implements RowMapper {public static final String QUERY_SQL = "SELECT  u.USER_ID,u.USER_NAME,u.ENCRYTED_PASSWORD  FROM login_user u";@Overridepublic User mapRow(ResultSet rs, int rowNum) throws SQLException {Long userId = rs.getLong("USER_ID");String userName = rs.getString("USER_NAME");String encrytedPassword = rs.getString("ENCRYTED_PASSWORD");return new User(userId, userName, encrytedPassword);}}

DAO(数据访问对象)类是用于访问数据库的类,例如查询、插入、更新、删除。

DAO类通常由@Repository注释,以告诉Spring“让我们把它们作为Spring bean来管理”。

UserDAO类用于操作LOGIN_USER表。它有一个在数据库中找到与用户名对应的用户的方法。

UserDao.java

package me.laocat.login.dao;import javax.sql.DataSource;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.dao.EmptyResultDataAccessException;import org.springframework.jdbc.core.support.JdbcDaoSupport;import org.springframework.stereotype.Repository;import org.springframework.transaction.annotation.Transactional;import me.laocat.login.mapper.UserMapper;import me.laocat.login.model.User;/** * 描述: 用户持久层操作类 * @author laocat */@Repository@Transactionalpublic class UserDao extends JdbcDaoSupport {/** * 注入数据源 */@Autowiredpublic UserDao(DataSource dataSource) {this.setDataSource(dataSource);}/** * 根据用户名称查询用户 * @param userName * @return */public User findUserByUserName(String userName) {// 查询sqlString sql = UserMapper.QUERY_SQL  + " WHERE u.USER_NAME = ? ";// 参数 Object[] params = new Object[] { userName };UserMapper userMapper = new UserMapper();try {User user = this.getJdbcTemplate().queryForObject(sql, params, userMapper);return user;} catch (EmptyResultDataAccessException e) {return null;}}}

RoleDao.java

package me.laocat.login.dao;import java.util.List;import javax.sql.DataSource;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.jdbc.core.support.JdbcDaoSupport;import org.springframework.stereotype.Repository;import org.springframework.transaction.annotation.Transactional;/** * 描述:角色持久层操作类 * @author laocat */@Repository@Transactionalpublic class RoleDao extends JdbcDaoSupport {@Autowiredpublic RoleDao(DataSource dataSource) {this.setDataSource(dataSource);}/** * 根据用户ID查询所有角色名称 * @param userId * @return */public List findRoleByUserId(Long userId) {// 查询sqlString sql = "SELECT r.ROLE_NAME "+ " FROM user_role ur, login_role r " + " WHERE ur.ROLE_ID = r.ROLE_ID AND ur.USER_ID = ? ";// 参数Object[] params = new Object[] { userId };List roles = this.getJdbcTemplate().queryForList(sql, params, String.class);return roles;}}

WebUtils.java

package me.laocat.login.utils;import java.util.Collection;import org.springframework.security.core.GrantedAuthority;import org.springframework.security.core.userdetails.User;public class WebUtils {public static String toString(User user) {StringBuilder sb = new StringBuilder();sb.append("UserName:").append(user.getUsername());Collection authorities = user.getAuthorities();if (authorities != null && !authorities.isEmpty()) {sb.append(" (");boolean first = true;for (GrantedAuthority a : authorities) {if (first) {sb.append(a.getAuthority());first = false;} else {sb.append(", ").append(a.getAuthority());}}sb.append(")");}return sb.toString();}}

EncrytedPasswordUtils.java

package me.laocat.login.utils;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;/** * 描述:密码加密工具类  * @author laocat */public class EncrytedPasswordUtils {//使用BCryptPasswordEncoder完成加密public static String encrytePassword(String password) {BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();return encoder.encode(password);}}

19.8 UserDetailsService

UserDetailsS​ervice是Spring Security中的核心接口。搜索“用户帐户和该用户的角色”是一项服务。用户每次登录系统时,Spring Security都会使用它。因此,您需要编写一个类来实现此接口。

在这里,我创建了实现UserDetailsS​​ervice接口的UserDetailsS​​erviceImpl类。 @Service注释了UserDetailsS​​erviceImpl类,以告诉Spring“让我们将其作为Spring bean进行管理”。

UserDetailsServiceImpl.java

package me.laocat.login.service.impl;import java.util.ArrayList;import java.util.List;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.security.core.GrantedAuthority;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.stereotype.Service;import me.laocat.login.dao.RoleDao;import me.laocat.login.dao.UserDao;import me.laocat.login.model.User;/** * 描述:自定义UserDetailsService类 *  * @author laocat */@Servicepublic class UserDetailsServiceImpl implements UserDetailsService {private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class);@Autowiredprivate UserDao userDao;@Autowiredprivate RoleDao roleDao;/** * 实现根据用户名称加载用户方法 */@Overridepublic UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {// 根据用户名称查询用户User user = userDao.findUserByUserName(userName);if (user == null) {log.info("User not found! {}", userName);throw new UsernameNotFoundException("User " + userName + " was not found in the database");}log.info("Found user:{}", user);// 根据用户查询他所具有的所有角色名称List roleNames = roleDao.findRoleByUserId(user.getUserId());// 授权列表List grantList = new ArrayList();// 如果角色名称不为空// 根据角色创建授权if (roleNames != null) {for (String roleName : roleNames) {// 创建授权GrantedAuthority authority = new SimpleGrantedAuthority(roleName);grantList.add(authority);}}UserDetails userDetails = (UserDetails) new org.springframework.security.core.userdetails.User(user.getUserName(), user.getEncrytedPassword(), grantList);return userDetails;}}

19.9 Controllers

WebController.java

package me.laocat.login.web;import java.security.Principal;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.security.core.Authentication;import org.springframework.security.core.userdetails.User;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import me.laocat.login.utils.WebUtils;/** * 描述:web控制类 *  * @author laocat */@Controllerpublic class WebController {private static final Logger log = LoggerFactory.getLogger(WebController.class);/** * 跳转到欢迎页面 *  * @param model * @return */@RequestMapping(value = { "/", "/welcome" }, method = RequestMethod.GET)public String welcome(Model model) {model.addAttribute("title", "欢迎页面");model.addAttribute("content", "这是欢迎页面!");return "welcome";}/** * 跳转到管理页面(只有admin角色用户有权访问) *  * @param model * @param principal * @return */@RequestMapping(value = "/admin", method = RequestMethod.GET)public String admin(Model model, Principal principal) {User loginUser = (User) ((Authentication) principal).getPrincipal();String userInfo = WebUtils.toString(loginUser);model.addAttribute("userInfo", userInfo);return "admin";}/** * 跳转到登陆页面 *  * @param model * @return */@RequestMapping(value = "/login", method = RequestMethod.GET)public String login(Model model) {return "login";}/** * 跳转到退出成功页面 *  * @param model * @return */@RequestMapping(value = "/logoutSuccessful", method = RequestMethod.GET)public String logoutSuccessful(Model model) {model.addAttribute("title", "退出成功页面");return "logoutSuccessful";}/** * 跳转到当前登陆用户信息页面 *  * @param model * @param principal * @return */@RequestMapping(value = "/userInfo", method = RequestMethod.GET)public String userInfo(Model model, Principal principal) {// 用户登录成功后。// 获取当事人用户名称String userName = principal.getName();log.info("User Name:{}", userName);// 获取当前登录用户User loginedUser = (User) ((Authentication) principal).getPrincipal();String userInfo = WebUtils.toString(loginedUser);model.addAttribute("userInfo", userInfo);return "userinfo";}/** * 拒绝访问跳转的页面 *  * @param model * @param principal * @return */@RequestMapping(value = "/403", method = RequestMethod.GET)public String accessDenied(Model model, Principal principal) {if (principal != null) {User loginedUser = (User) ((Authentication) principal).getPrincipal();String userInfo = WebUtils.toString(loginedUser);model.addAttribute("userInfo", userInfo);String content = "你好 " + principal.getName() //+ "
您没有访问此页面的权限!";model.addAttribute("content", content);}return "403";}}

19.10 Thymeleaf模板页面

_menu.html作为网站页面内部的一部分。将其嵌套再其他页面中作为菜单。

_menu.html

首页 | 用户信息 | 管理页面 | 退出

welcome.html

         

内容 :

login.html

         Login

Login

登陆失败!!!
Reason : 静态摘要

输入用户名和密码:

用户名: 密码:
用户名/密码:
user1/123 admin1/123

logoutSuccessful.html

         Logout

退出成功!

userInfo.html

         User Info

用户信息页面

欢迎 :

这是受保护的页面!

admin.html

         

管理页面

欢迎 :

这是受保护的页面!

如果用户登录了系统,但是访问了一个未授权的功能页面(不是他们的角色),系统将显示/403页面的内容来通知访问被拒绝。

403.html

  403

19.11 运行应用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值