一、前言
本案例中的源代码已上传到资源库,可自行下载,传送阵 https://download.csdn.net/download/qq_36260963/89906196
Spring Boot是为了简化Spring应用的创建、运行、调试、部署等而出现的,使用它可以做到专注于Spring应用的开发,而无需过多关注XML的配置。学习框架就是学习配置
简单来说,它提供了一堆依赖打包Starter,并已经按照使用习惯解决了依赖问题—习惯大于约定。Spring Boot默认使用tomcat作为服务器,使用logback提供日志记录。无需多言,直接进入节奏.
Thymeleaf是一个流行的现代服务器端Java模板引擎,它专门设计用于Web和独立环境中的应用程序。它允许开发者以清晰和直观的方式将服务器端的数据与HTML、XML、JavaScript、CSS以及纯文本等模板文件结合起来。Thymeleaf的最大特点之一是它的“自然模板”技术,这意味着开发者可以编写标准的HTML代码,并通过Thymeleaf特有的属性和表达式(如th:text、th:if等)来动态插入或修改内容,而无需改变HTML的结构或引入特定的模板语法。
Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。
二、效果演示
密码错误
登录成功
三、后端代码
四、springsecurity 相关
主要是springsecurity config 配置相关,包含开启表单登录,url地址拦截与放行,退出登录;以及自定义userdetailservice 接口,实现自己的登录逻辑
4.1 springsecurity 核心配置
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {private final Logger logger = LoggerFactory.getLogger(this.getClass());@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().mvcMatchers("/user/**","/","/login","/register").permitAll() //允许访问.anyRequest().authenticated() // 其他都需要认证.and().formLogin() // 开启表单登录.loginProcessingUrl("/doLogin").usernameParameter("username").passwordParameter("passwd").loginPage("/login").successForwardUrl("/employee/lists")// 成功后跳转的url.failureUrl("/login").and().logout().logoutSuccessUrl("/login")//退出登录后跳转的页面.and().csrf().disable(); // 关闭csrf 防护}}
4.2 用户登录相关
@Service
public class UserServiceImpl implements UserService, UserDetailsService {@Autowired(required = false)private UserMapper userMapper;private final Logger logger = LoggerFactory.getLogger(this.getClass());@Overridepublic User login(String username, String password) throws IllegalAccessException {User queryUsr = userMapper.findByUserName(username);if (!StringUtils.hasLength(username) || !StringUtils.hasLength(password)) {throw new IllegalAccessException("用户名和密码不能为空");}if (ObjectUtils.isEmpty(queryUsr)) {throw new IllegalAccessException("用户不存在");}String encPwd = DigestUtils.md5DigestAsHex(password.getBytes(StandardCharsets.UTF_8));logger.info("原密码:{},加密后的密码为:{}",password,encPwd);// 密码对比DigestUtilsif (!encPwd.equals(queryUsr.getPassword())) {throw new IllegalAccessException("密码错误!");}return queryUsr;}@Overridepublic void addUser(User user) throws IllegalAccessException{if (!StringUtils.hasLength(user.getUsername()) || !StringUtils.hasLength(user.getPassword())) {throw new IllegalAccessException("用户名和密码不能为空");}//查看用户名是否重复User queryUser = userMapper.findByUserName(user.getUsername());if (!ObjectUtils.isEmpty(queryUser)) {throw new IllegalAccessException("用户【"+user.getUsername()+"】已存在,请更换用户名!");}String password = user.getPassword();String encPwd = DigestUtils.md5DigestAsHex(password.getBytes(StandardCharsets.UTF_8));user.setPassword(encPwd);//添加userMapper.save(user);logger.info("添加用户成功!");}@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {if (!StringUtils.hasLength(username) ) {throw new UsernameNotFoundException("用户名不能为空");}User queryUsr = userMapper.findByUserName(username);if (ObjectUtils.isEmpty(queryUsr)) {throw new UsernameNotFoundException("用户不存在");}LoginSessionUser sessionUser = new LoginSessionUser(queryUsr);return sessionUser;}
}
4.3 userdetail 实体类
public class LoginSessionUser implements UserDetails {private User user;public LoginSessionUser(User user) {this.user = user;}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return Arrays.asList(new SimpleGrantedAuthority("ROLE_amin"));}@Overridepublic String getPassword() {return "{MD5}"+user.getPassword();}@Overridepublic String getUsername() {return user.getUsername();}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}
}
五、spring mvc 配置
用来配置静态资源拦截,常规url 以及viername配置
5.1 mvc 核心配置
@Configuration
public class MvcConfig implements WebMvcConfigurer {@Value("${photo.file.dir}")private String dir;@Overridepublic void addViewControllers(ViewControllerRegistry registry) {//viewController 请求路径 viewName: 跳转视图registry.addViewController("/").setViewName("redirect:/login");registry.addViewController("/login").setViewName("login");registry.addViewController("/register").setViewName("regist");registry.addViewController("/addEmp").setViewName("addEmp");}@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {//registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");registry.addResourceHandler("/**").addResourceLocations("classpath:/static/**").addResourceLocations("file:"+dir);}}
六、yml 配置相关
1、数据库相关配置
2、日志配置
3、静态文件配置
4、模版解析器 thymeleaf 配置
server:port: 8082spring:## 数据库配置datasource:driver-class-name: com.mysql.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSourceusername: rootpassword: 123456url: jdbc:mysql://localhost:3306/springboot_ems_db?characterEncoding=UTF-8web:#静态文件配置resources:static-locations: classpath:/static/,file:${photo.file.dir}mvc:static-path-pattern: /static/**# thymeleaf 模版配置thymeleaf:cache: falsesuffix: .htmlprefix: classpath:/templates/mode: html# mybatis 配置 mybatis:mapper-locations: classpath:/mapper/mysql/*.xmltype-aliases-package: com.fashion.entity# 日志配置 logging:level:com.fashion: debugphoto:file:dir: j:\java\project\springboot-study\springboot-ems-security\images\
七、前端相关页面
这里只是展示部分thymeleaf 的部分,因为过多页面,需要可自行下载zip文件包
7.1 登录页面
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="en" xmlns:th="http://www.thymeleaf.org"><head><title>login</title><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" th:href="@{/css/style.css}" /></head><body><div id="wrap"><div id="top_content"><div id="header"><div id="rightheader"><p><span th:text="${#dates.format(#dates.createNow(), 'yyyy-MM-dd HH:mm:ss')}"/><br /></p></div><div id="topheader"><h1 id="title"><a th:href="@{/login }">main</a></h1></div><div id="navigation"></div></div><div id="content"><p id="whereami"></p><h1>欢迎进入,请登录!<!--<span th:text="${session.SPRING_SECURITY_LAST_EXCEPTION }" style="color: red;"/><span th:text="${session.errMsg }" style="color: deeppink;"/>--><span th:if="${session.SPRING_SECURITY_LAST_EXCEPTION != null}" th:text="${session.SPRING_SECURITY_LAST_EXCEPTION.message }" style="color: deeppink;"/></h1><form th:action="@{/doLogin }" method="post"><table cellpadding="0" cellspacing="0" border="0"class="form_table"><tr><td valign="middle" align="right">用户名:</td><td valign="middle" align="left"><input type="text" class="inputgri" name="username" /></td></tr><tr><td valign="middle" align="right">密码:</td><td valign="middle" align="left"><input type="password" class="inputgri" name="passwd" /></td></tr></table><p><input type="submit" class="button" value="点我登录 »" /> <a th:href="@{/register}">还没有账号,立即注册</a></p></form></div></div><div id="footer"><div id="footer_bg">ABC@126.com</div></div></div></body>
</html>
7.2 登录成页面
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extrasspringsecurity5"><head><title>emplist</title><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><link rel="stylesheet" type="text/css" th:href="@{/css/style.css}" /></head><body><div id="wrap"><div id="top_content"> <div id="header"><div id="rightheader"><p><span th:text="${#dates.format(#dates.createNow(), 'yyyy-MM-dd HH:mm:ss')}"/><br /><span sec:authorize="isAuthenticated()"><a th:href="@{/logout }">安全退出</a></span></p></div><div id="topheader"><h1 id="title"><a th:href="@{/employee/lists }">main</a></h1></div><div id="navigation"></div></div><div id="content"><p id="whereami"></p><h1>欢迎<span sec:authorize="isAuthenticated()"><span sec:authentication="principal.username"></span></span></h1><table class="table"><tr class="table_header"><td>编号</td><td>姓名</td><td>头像</td><td>工资</td><td>生日</td><td>操作</td></tr><tr th:each="emp,status:${employeeList }" th:class="${status.odd ? 'row1' : 'row2'}"><td><span th:text="${emp.id }"/></td><td><span th:text="${emp.name }"/></td><td><img th:src="@{/ }+${emp.photo}" width="60"></td><td><span th:text="${emp.salary }"/></td><td><span th:text="${#dates.format(emp.birthday,'yyyy年MM月hh日')}"/></td><td><a href="javascript:;" th:onclick="'delFn('+${emp.id}+');'">删除</a> <a href="javascript:;" th:onclick="'updFn('+${emp.id }+');'">更新</a></td></tr></tr></table><script type="text/javascript">function delFn(id) {if (confirm("你真的要删除员工id为:"+id+"的记录吗?")) {location.href = '[[@{/employee/delEmployee?id= }]]'+id;}}function updFn(id) {location.href = '[[@{/employee/getDetail?id= }]]'+id;}</script><p><!--<input type="button" class="button" value="添加" onclick="location='addEmp.html'"/>--><input type="button" class="button" value="添加" onclick="addEmp()"/><script type="text/javascript">function addEmp() {location.href = '[[@{/addEmp}]]'}</script></p></div></div><div id="footer"><div id="footer_bg">ABC@126.com</div></div></div></body>
</html>