当前位置:   article > 正文

SpringSecurity+Mybatis实现用户自助注册登录(含角色),打造简单安全的注册登录页面。_userdetailsservice注册

userdetailsservice注册

#项目架构、功能点

架构: 

  • Springboot2.5.+
  • MySQL数据库8.0+(记录用户信息、角色清单、用户角色对照表)
  • 持久层Mybatis
  • 用户注册页面RegisterPage采用Thymeleaf动态网页
  • 登录页面Login采用SpringSecurity标准表单登录网页

功能点:

  • 实现用户自助注册登录
  • 实现用户自助选择角色
  • 用户信息、角色清单、用户角色对照表采用SpringSecurity标准架构
  • 用户密码采用SpringSecurity首推的BCryptPasswordEncoder加密算法记录
  • 实现登陆后URL访问与角色对应

#用户自助注册及登录流程图

#项目文件结构、数据库设计、Maven依赖、aplication配置

文件结构:

数据库设计:

user表,记录用户信息  

role表,记录角色清单

user_role表,用户角色对照表

Maven依赖: 

  1. <!--Spring Security-->
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-security</artifactId>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.springframework.boot</groupId>
  8. <artifactId>spring-boot-starter-web</artifactId>
  9. </dependency>
  10. <!--mybatis-->
  11. <dependency>
  12. <groupId>org.mybatis.spring.boot</groupId>
  13. <artifactId>mybatis-spring-boot-starter</artifactId>
  14. <version>2.2.0</version>
  15. </dependency>
  16. <!--Mysql-->
  17. <dependency>
  18. <groupId>mysql</groupId>
  19. <artifactId>mysql-connector-java</artifactId>
  20. <scope>runtime</scope>
  21. </dependency>
  22. <!--Thymeleaf-->
  23. <dependency>
  24. <groupId>org.springframework.boot</groupId>
  25. <artifactId>spring-boot-starter-thymeleaf</artifactId>
  26. </dependency>

因为使用Mybatis,所以build要添加XML资源目录,否则编译会缺少UserMapper.XML。

  1. <resources>
  2. <resource>
  3. <!-- XML默认是放在resource下面,如果有自定义,需要把resource加上 -->
  4. <directory>src/main/java</directory>
  5. <includes>
  6. <include>**/*.xml</include>
  7. </includes>
  8. </resource>
  9. <resource>
  10. <directory>src/main/resources</directory>
  11. </resource>
  12. </resources>

aplication配置:

  1. #datasource mybatis配置--------------------------------
  2. spring.datasource.url=jdbc:mysql://127.0.0.1:3306/jpa?characterEncoding=UTF-8
  3. spring.datasource.username=root
  4. spring.datasource.password=12345678
  5. spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
  6. #datasource mybatis配置--------------------------------
  7. #thymeleaf配置--------------------------------
  8. spring.thymeleaf.cache=true
  9. spring.thymeleaf.checktemplate=true
  10. spring.thymeleaf.check-template-location=true
  11. spring.thymeleaf.encoding=UTF-8
  12. spring.thymeleaf.servlet.content-type=text/html
  13. spring.thymeleaf.suffix=.html
  14. spring.thymeleaf.prefix=classpath:/templates/
  15. #Thymeleaf配置--------------------------------

#用户前端注册页面registerPage.html代码(Thymeleaf模板动态网页)

这里通过用户注册接口registerController去数据库查询角色清单(role表),将查询结果写入roles集合中,通过Thymeleaf模板进行调用并加入select-option选择列表,供用户注册时选择某一角色。

  1. <!DOCTYPE html>
  2. <html lang="en" xmlns:th="http://www.thymeleaf.org">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>新用户注册</title>
  6. </head>
  7. <body>
  8. <h2 align="center">新用户注册</h2>
  9. <hr width="40%" color="gray"/>
  10. <form action="/doregister" method="post" enctype="application/x-www-form-urlencoded">
  11. <table width="40%" bgcolor="gray" align="center">
  12. <tr>
  13. <td align="right">用户名:</td>
  14. <td>
  15. <input type="text" name="username" size="25px" maxlength="10" placeholder="请输入用户名最长10位" required/>
  16. </td>
  17. </tr>
  18. <tr>
  19. <td align="right">密码:</td>
  20. <td>
  21. <input type="password" name="password" size="25px" maxlength="20" placeholder="请输入密码最长20位" required>
  22. </td>
  23. </tr>
  24. <tr>
  25. <td align="right">角色:</td>
  26. <td>
  27. <label>
  28. <select name="role">
  29. <option>--请选择角色--</option>
  30. <option th:each="role:${roles}"
  31. th:value="${role.id}"
  32. th:text="${role.nameZH}"
  33. th:selected="${role==role.id}"></option>
  34. </select>
  35. </label>
  36. </td>
  37. </tr>
  38. <tr>
  39. <td align="right">
  40. <input type="submit" value="提交注册">
  41. </td>
  42. <td>
  43. </td>
  44. </tr>
  45. </table>
  46. </form>
  47. </body>
  48. </html>

 #实体类、Mybatis持久层接口、用户服务类、用户注册及登录接口 

角色实体类Role:

  1. package com.example.springsecurity.Entity;
  2. import java.io.Serializable;
  3. public class Role implements Serializable {
  4. private Integer id;
  5. private String name;
  6. private String nameZH;
  7. public Integer getId() {
  8. return id;
  9. }
  10. public void setId(Integer id) {
  11. this.id = id;
  12. }
  13. public String getName() {
  14. return name;
  15. }
  16. public void setName(String name) {
  17. this.name = name;
  18. }
  19. public String getNameZH() {
  20. return nameZH;
  21. }
  22. public void setNameZH(String nameZH) {
  23. this.nameZH = nameZH;
  24. }
  25. }

用户实体类User(包含所具有的角色) :

该实体类主要用于用户登录,需要使用标准的UserDetails接口。

  1. package com.example.springsecurity.Entity;
  2. import org.springframework.security.core.GrantedAuthority;
  3. import org.springframework.security.core.authority.SimpleGrantedAuthority;
  4. import org.springframework.security.core.userdetails.UserDetails;
  5. import java.util.ArrayList;
  6. import java.util.Collection;
  7. import java.util.List;
  8. public class User implements UserDetails {
  9. private Integer id;
  10. private String username;
  11. private String password;
  12. private Boolean enabled;
  13. private Boolean locked;
  14. private List<Role> roles;
  15. @Override
  16. public Collection<? extends GrantedAuthority> getAuthorities(){
  17. List<SimpleGrantedAuthority> authorities= new ArrayList<>();
  18. for (Role r:roles){
  19. authorities.add(new SimpleGrantedAuthority(r.getName()));
  20. }
  21. return authorities;
  22. }
  23. @Override
  24. public String getPassword(){
  25. return password;
  26. }
  27. @Override
  28. public String getUsername(){
  29. return username;
  30. }
  31. @Override
  32. public boolean isAccountNonExpired(){
  33. return true;
  34. }
  35. @Override
  36. public boolean isAccountNonLocked(){
  37. return true;
  38. }
  39. @Override
  40. public boolean isCredentialsNonExpired(){
  41. return true;
  42. }
  43. @Override
  44. public boolean isEnabled(){
  45. return true;
  46. }
  47. public Integer getId() {
  48. return id;
  49. }
  50. public void setId(Integer id) {
  51. this.id = id;
  52. }
  53. public void setUsername(String username) {
  54. this.username = username;
  55. }
  56. public void setPassword(String password) {
  57. this.password = password;
  58. }
  59. public Boolean getEnabled() {
  60. return enabled;
  61. }
  62. public void setEnabled(Boolean enabled) {
  63. this.enabled = enabled;
  64. }
  65. public Boolean getLocked() {
  66. return locked;
  67. }
  68. public void setLocked(Boolean locked) {
  69. this.locked = locked;
  70. }
  71. public List<Role> getRoles() {
  72. return roles;
  73. }
  74. public void setRoles(List<Role> roles) {
  75. this.roles = roles;
  76. }
  77. }

用户注册实体类UserRegister(包含用户需要申请的角色) : 

该实体类主要用于用户注册。

  1. package com.example.springsecurity.Entity;
  2. public class UserRegister {
  3. private Integer id;
  4. private String username;
  5. private String password;
  6. private Boolean enabled;
  7. private Boolean locked;
  8. private Integer role;
  9. public Integer getId() {
  10. return id;
  11. }
  12. public void setId(Integer id) {
  13. this.id = id;
  14. }
  15. public String getUsername() {
  16. return username;
  17. }
  18. public void setUsername(String username) {
  19. this.username = username;
  20. }
  21. public String getPassword() {
  22. return password;
  23. }
  24. public void setPassword(String password) {
  25. this.password = password;
  26. }
  27. public Boolean getEnabled() {
  28. return enabled;
  29. }
  30. public void setEnabled(Boolean enabled) {
  31. this.enabled = enabled;
  32. }
  33. public Boolean getLocked() {
  34. return locked;
  35. }
  36. public void setLocked(Boolean locked) {
  37. this.locked = locked;
  38. }
  39. public Integer getRole() {
  40. return role;
  41. }
  42. public void setRole(Integer role) {
  43. this.role = role;
  44. }
  45. }

Mybatis持久层接口:

主要包含UserMapper.xml和UserMapper interface,包含:

loadUserByUsername方法,通过用户名查询用户

getUserRolesByUid方法,通过用户ID获取用户所有角色

addUserByUsername方法,通过UserRegister实体类注册新用户

getAllRole方法,获取当前系统所有角色

addRole方法,通过用户ID和角色ID,给用户添加角色

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE mapper
  3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  5. <mapper namespace="com.example.springsecurity.Repository.UserMapper">
  6. <select id="loadUserByUsername" resultType="com.example.springsecurity.Entity.User">
  7. select * from user where username=#{username}
  8. </select>
  9. <select id="getUserRolesByUid" resultType="com.example.springsecurity.Entity.Role">
  10. select * from role r,user_role ur where r.id=ur.rid and ur.uid=#{id}
  11. </select>
  12. <insert id="addUserByUsername" parameterType="com.example.springsecurity.Entity.UserRegister">
  13. insert into user(username,password,enabled,locked) values(#{username},#{password},#{enabled},#{locked})
  14. </insert>
  15. <select id="getAllRole" resultType="com.example.springsecurity.Entity.Role">
  16. select * from role
  17. </select>
  18. <insert id="addRole">
  19. insert into user_role(uid,rid) values(#{uid},#{rid})
  20. </insert>
  21. </mapper>
  1. package com.example.springsecurity.Repository;
  2. import com.example.springsecurity.Entity.Role;
  3. import com.example.springsecurity.Entity.User;
  4. import com.example.springsecurity.Entity.UserRegister;
  5. import org.apache.ibatis.annotations.Mapper;
  6. import java.util.List;
  7. @Mapper
  8. public interface UserMapper {
  9. User loadUserByUsername(String username);
  10. List<Role> getUserRolesByUid(Integer id);
  11. int addUserByUsername(UserRegister userRegister);
  12. List<Role> getAllRole();
  13. int addRole(Integer uid,Integer rid);
  14. }

用户服务类UserService:

是上述接口方法的实现类,包含:

loadUserByUsername实现方法,用于查询用户以及所具有的角色

addUserByUsername实现方法,用于实现用户注册以及角色注册

getAllRole实现方法,用户查询所有角色清单

  1. package com.example.springsecurity.Service;
  2. import com.example.springsecurity.Entity.Role;
  3. import com.example.springsecurity.Entity.User;
  4. import com.example.springsecurity.Entity.UserRegister;
  5. import com.example.springsecurity.Repository.UserMapper;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.security.core.userdetails.UserDetails;
  8. import org.springframework.security.core.userdetails.UserDetailsService;
  9. import org.springframework.security.core.userdetails.UsernameNotFoundException;
  10. import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
  11. import org.springframework.stereotype.Service;
  12. import java.util.List;
  13. @Service
  14. public class UserService implements UserDetailsService {
  15. @Autowired
  16. UserMapper userMapper;
  17. @Override
  18. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException{
  19. User user = userMapper.loadUserByUsername(username);
  20. if(user == null){
  21. throw new UsernameNotFoundException("账户不存在!");
  22. }
  23. user.setRoles(userMapper.getUserRolesByUid(user.getId()));
  24. return user;
  25. }
  26. public String addUserByUsername(UserRegister userRegister){
  27. User newuser = userMapper.loadUserByUsername(userRegister.getUsername());
  28. if (newuser != null){
  29. return "账户存在,注册失败!";
  30. }else {
  31. //新用户密码采用BCryptPasswordEncoder(10)格式存入数据库
  32. userRegister.setPassword(new BCryptPasswordEncoder(10).encode(userRegister.getPassword()));
  33. //设置用户状态可用,没有锁定
  34. userRegister.setEnabled(true);
  35. userRegister.setLocked(false);
  36. //执行用户注册
  37. int adduser = userMapper.addUserByUsername(userRegister);
  38. //用户成功注册后,添加用户角色
  39. if(adduser > 0){
  40. User getuser =userMapper.loadUserByUsername(userRegister.getUsername());
  41. int addrole = userMapper.addRole(getuser.getId(),userRegister.getRole());
  42. if (addrole > 0){
  43. return "账户注册成功,角色注册成功!";
  44. }else{
  45. return "账户注册成功!角色注册失败!";
  46. }
  47. }else {
  48. return "账户注册失败!";
  49. }
  50. }
  51. }
  52. public List<Role> getAllRole(){
  53. return userMapper.getAllRole();
  54. }
  55. }

登录接口:

登录接口包含:

registerController用户注册接口

doRegisterController执行用户注册接口及返回注册结果

HelloController登录接口

  1. package com.example.springsecurity.Controller;
  2. import com.example.springsecurity.Service.UserService;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.stereotype.Controller;
  5. import org.springframework.web.bind.annotation.GetMapping;
  6. import org.springframework.web.servlet.ModelAndView;
  7. @Controller
  8. public class registerController {
  9. @Autowired
  10. UserService userService;
  11. @GetMapping("/register")
  12. //registerController用户注册接口,将所有角色信息数据库取值并绑定roles赋给前端registerPage.html
  13. public ModelAndView resgister(){
  14. ModelAndView mv = new ModelAndView();
  15. mv.addObject("roles",userService.getAllRole());
  16. mv.setViewName("registerPage");
  17. return mv;
  18. }
  19. }
  1. package com.example.springsecurity.Controller;
  2. import com.example.springsecurity.Entity.UserRegister;
  3. import com.example.springsecurity.Service.UserService;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.web.bind.annotation.PostMapping;
  6. import org.springframework.web.bind.annotation.RestController;
  7. @RestController
  8. public class doRegisterController {
  9. @Autowired
  10. UserService userService;
  11. @PostMapping("/doregister")
  12. //doRegisterController执行用户注册接口及返回注册结果
  13. public String doregister(UserRegister userRegister){
  14. return userService.addUserByUsername(userRegister);
  15. }
  16. }
  1. package com.example.springsecurity.Controller;
  2. import org.springframework.web.bind.annotation.GetMapping;
  3. import org.springframework.web.bind.annotation.RestController;
  4. @RestController
  5. //HelloController登录接口,URL访问权限控制,分别对应不同的角色方能访问
  6. public class HelloController {
  7. @GetMapping("/admin/hello")
  8. public String admin(){
  9. return "hello admin";
  10. }
  11. @GetMapping("/user/hello")
  12. public String user(){
  13. return "hello user";
  14. }
  15. @GetMapping("/db/hello")
  16. public String dba(){
  17. return "hello dba";
  18. }
  19. @GetMapping("/hello")
  20. public String hello(){
  21. return "hello";
  22. }
  23. }

#Spring Security核心配置类WebSecurityLoginConfig

①注入用户服务类用于登录验证。

②配置用户登录密码需要BCryptPasswordEncoder(10)密文认证。

③对可访问资源URL限定固定的角色方能访问。

④用户注册接口和执行用户注册接口允许访问。

⑤成功登陆后跳转hello接口。

  1. package com.example.springsecurity.Config;
  2. import com.example.springsecurity.Service.UserService;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.context.annotation.Bean;
  5. import org.springframework.context.annotation.Configuration;
  6. import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
  7. import org.springframework.security.config.annotation.web.builders.HttpSecurity;
  8. import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
  9. import org.springframework.security.core.Authentication;
  10. import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
  11. import org.springframework.security.crypto.password.PasswordEncoder;
  12. import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
  13. import javax.servlet.http.HttpServletRequest;
  14. import javax.servlet.http.HttpServletResponse;
  15. import java.io.IOException;
  16. @Configuration
  17. public class WebSecurityLoginConfig extends WebSecurityConfigurerAdapter {
  18. //注入用户服务
  19. @Autowired
  20. UserService userService;
  21. //配置用户登录密码需要BCryptPasswordEncoder密文认证
  22. @Bean
  23. PasswordEncoder passwordEncoder(){
  24. return new BCryptPasswordEncoder(10);
  25. }
  26. //基于数据库的用户账号密码、角色、过期、锁定等认证
  27. @Override
  28. protected void configure(AuthenticationManagerBuilder auth) throws Exception{
  29. auth.userDetailsService(userService);
  30. }
  31. @Override
  32. protected void configure(HttpSecurity httpSecurity) throws Exception {
  33. httpSecurity.authorizeRequests()
  34. //对可访问URL资源进行角色控制
  35. .antMatchers("/admin/**")
  36. .hasRole("admin")
  37. .antMatchers("/user/**")
  38. .access("hasAnyRole('admin','user')")
  39. .antMatchers("/db/**")
  40. .access("hasRole('dba') and hasRole('admin')")
  41. //用户注册接口和执行用户注册接口允许访问
  42. .antMatchers("/register","/doregister")
  43. .permitAll()
  44. //用户访问其他URL资源都必须认证后访问,即登陆后访问
  45. .anyRequest()
  46. .authenticated()
  47. //开启表单登录,即登录界面,登录URL为/login,登录参数用户名username密码password
  48. //Ajax或移动端通过POST请求登录,接口为/login,permitAll表示登录不需要认证即可访问
  49. .and()
  50. .formLogin()
  51. .loginProcessingUrl("/login")
  52. .permitAll()
  53. //成功登录后跳转到hello页面
  54. .successHandler(new AuthenticationSuccessHandler() {
  55. @Override
  56. public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
  57. response.setContentType("application/json;charset=utf-8");
  58. response.sendRedirect("/hello");
  59. }
  60. })
  61. .and()
  62. .csrf()
  63. .disable();
  64. }
  65. }

#启动数据库和项目,开始验证测试

第一步测试注册:

输入用户注册接口http://localhost:8080/register,自动跳转到registerPage.html,并将所有角色信息从数据库取出赋值给前端。

提交注册,跳转到http://localhost:8080/doregister接口,返回注册结果。

数据库中user表已经新增ceshi用户。

用户角色表user_role中新增数据:ceshi用户(uid:15)对应用户角色(rid:3)。 

 

 

第二步测试登录: 

我们输入登录网址http://localhost:8080/login或者任意输入后缀地址,均可以访问登录接口。

  

用ceshi用户登陆后,具有用户角色,可以访问/user/hello接口或者hello接口,当然系统会自动跳转到hello接口页面。

 

当我们访问管理员接口/admin/hello接口,系统会因为缺少权限而拒绝访问Forbidden。

#总结

SpringSecurity可以非常轻松的实现用户登录验证、注册、跳转、URL接口访问控制等。

Thymeleaf可以将后端数据赋值给前端,便于前端使用,非常适合做登录网页前端设计。

Mybatis持久层非常灵活,可以实现用户自助注册、角色赋值、用户查询、角色查询等各种方法并于数据库交互,实现存储加密。

通过以上技术,我们就可以轻松打造简单安全的注册登录页面了,而不去纠结于各项安全设置和各种接口的设计。 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/922024
推荐阅读
相关标签
  

闽ICP备14008679号