基于数据库的认证
2021/10/25 2:10:06
本文主要是介绍基于数据库的认证,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
在 WebSecurityConfig 添加@EnableGlobalMethodSecurity 注解开启方法的访问权限, 代码如下: @EnableWebSecurity //是 Spring Security 用于启用 Web 安全的注解 @EnableGlobalMethodSecurity(prePostEnabled = true) // 开启方法级安全验证 public class WebSecurityConfig extends WebSecurityConfigurerAdapter { //... }
代码解释: prePostEnabled=true 会解锁@PreAuthorize 和@PostAuthorize 两个注解, @PreAuthorize 注解会在方法执行前进行验证,而@PostAuthorize 注解在方法执行后进行验证。
在控制层添加访问接口 在 UserController 类中增加方法的访问权限: @RestController public class UserController { @Autowired private UserInfoService userInfoService; @GetMapping("/getUser") public UserInfo getUser(@RequestParam String username){ return userInfoService.getUserInfo(username); } @PreAuthorize("hasAnyRole('user')") // 只能 user 角色才能访问该方法 @GetMapping("/user") public String user(){ return "hello,user"; } @PreAuthorize("hasAnyRole('admin')") // 只能 admin 角色才能访问该方法 @GetMapping("/admin") public String admin(){ return "hello,admin"; } }
代码解释: @PreAuthorize("hasAnyRole('user')")注解表示访问该方法需要 USER 角色。
密码加密保存 上文中的用户密码都是手动在数据库添加的,所以数据库中是明文显示,在实际开发中, 都是需要加密保存的。下面模拟注册用户,加密保存密码:
修改 mapper 接口 在 UserMapper 接口中添加插入用户,代码如下: @Mapper @Repository public interface UserMapper { //... @Insert("insert into user(username, password, role) value(#{username}, #{password}, #{role})") int insertUserInfo(UserInfo userInfo); }
修改 service 类 在 UserInfoService 类中添加插入方法,并且密码要加密保护,代码如下: @Service public class UserInfoService { // ... @Autowired private PasswordEncoder passwordEncoder; // ... public int insertUser(UserInfo userInfo){ /* 加密密码*/ userInfo.setPassword(passwordEncoder.encode(userInfo.getPassword())); return userMapper.insertUserInfo(userInfo); } }
修改 controoler 在 UserController 类中添加插入用户接口,代码如下: @RestController public class UserController{ //... @PostMapping("/addUser") public int addUser(@RequestBody UserInfo userInfo){ return userInfoService.insertUser(userInfo); } }
添加失败,响应的状态码显示 401 Unauthorized, 说明无权限,需要登录,但注册用户是不用登录的,所以需要给注册用户释放权限。 修改 WebSecurityConfig 配置类,重写 configure(HttpSecurity http)方法,配置允许注册用户的请求访问:
@EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { //... @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() // 允许 post 请求/addUser,而无需认证 .antMatchers(HttpMethod.POST, "/addUser").permitAll() .anyRequest().authenticated() // 所有请求都需要验证 .and() .formLogin() // 使用默认的登录页面 .and() .csrf().disable(); // post 请求要关闭 csrf 验证,不然访问报错;实际开发中开启,需要前端配合传递其他参数 }}
使用加密密码登录 使用加密密码登录,需要修改 CustomUserDetailsService 类,之前从数据库拿到明文密码后需要加密,现在数据库里面的密码已经加密了,就不用加密了,代码如下:
@Component public class MyUserDatailService implements UserDetailsService { //... @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // ... return new User( userInfo.getUsername(), // 数据库密码已加密,不用再加密 userInfo.getPassword(), authorities ); } }
角色继承实际上是一个很常见的需求,因为大部分公司治理可能都是金字塔形的,上司 可能具备下属的部分甚至所有权限,这一现实场景,反映到我们的代码中,就是角色继承了。 Spring Security 中为开发者提供了相关的角色继承解决方案,只需要开发者在配置类中提供 一个 RoleHierarchy 即可,代码如下: @Bean RoleHierarchy roleHierarchy() { RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl(); String hierarchy = "ROLE_dba > ROLE_admin \n ROLE_admin > ROLE_user"; roleHierarchy.setHierarchy(hierarchy); return roleHierarchy; }
在这里我们提供了一个 RoleHierarchy 接口的实例,使用字符串来描述了角色之间的继承关系, ROLE_dba 具备 ROLE_admin 的所有权限,而 ROLE_admin 则具备 ROLE_user的所有权限。提供了这个 Bean 之后,以后所有具备 ROLE_user 角色才能访问的资源,ROLE_dba 和 ROLE_admin 也都能访问,具备 ROLE_amdin 角色才能访问的资源,ROLE_dba 也能访问。
<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>com.dsecurity</groupId> <artifactId>dsecurity</artifactId> <version>0.0.1-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.0.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <!-- 声明项目配置依赖编码格式为 utf-8 --> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <fastjson.version>1.2.24</fastjson.version> </properties> <dependencies> <!--Spring Data Jpa依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.3</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> <version>8.0.13</version><!--$NO-MVN-MAN-VER$ --> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build> </project>
server.port =8089 spring.datasource.url=jdbc:mysql://localhost:3306/springbootdb?serverTimezone=UTC&autoReconnect=true spring.datasource.username=root spring.datasource.password=admin spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver logging.level.com.example.bdatabaserole.mapper=debug mybatis.mapper-locations=classpath:mappers/*.xml mybatis.type-aliases-package=com.tszr.mapper mybatis.configuration.map-underscore-to-camel-case=true spring.jpa.database=MySQL spring.jpa.show-sql=true spring.jpa.hibernate.ddl-auto=create-drop spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
<?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"> <!--命名空间必须和UserMapper全类名相同 --> <mapper namespace="com.tszr.mapper.UserMapper"> <select id="getUserByUsername" resultType="com.tszr.entity.User"> select * from user where username = #{username}; </select> <select id="getRolesById" resultType="com.tszr.entity.Role"> select * from role where id in(select rid from user_role where uid=#{uid}); </select> </mapper>
package com.tszr.entity; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.JoinColumn; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.ManyToOne; import javax.persistence.Table; @Entity @Table(name = "role") public class Role { public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getNameZh() { return nameZh; } public void setNameZh(String nameZh) { this.nameZh = nameZh; } @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @ManyToOne(cascade=CascadeType. REFRESH ,optional= false) @ JoinColumn (name = "user_id" ) private int id; @Column(name = "name", unique = true, nullable = false, length = 64) private String name; @Column(name = "nameZh", nullable = false, length = 64) private String nameZh; }
package com.tszr.entity; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import com.fasterxml.jackson.annotation.JsonIgnore; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.List; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name = "user") public class User implements Serializable, UserDetails { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; public int getId() { return id; } public void setId(int 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 List<Role> getRoleList() { return roleList; } public void setRoleList(List<Role> roleList) { this.roleList = roleList; } @Column(name = "username", unique = true, nullable = false, length = 64) private String username; @Column(name = "password", nullable = false, length = 64) private String password; @JsonIgnore private List<Role> roleList; @Override public Collection<? extends GrantedAuthority> getAuthorities() { Collection<GrantedAuthority> authorities = new ArrayList<>(); for (Role role : roleList) { // 数据库role表字段中是以ROLE_开头的,所以此处不必再加ROLE_ authorities.add(new SimpleGrantedAuthority(role.getName())); } return authorities; } /** * 指示用户的账户是否已过期。无法验证过期的账户。 如果用户的账户有效(即未过期),则返回true,如果不在有效就返回false */ @Override public boolean isAccountNonExpired() { return true; } /** * 指示用户是锁定还是解锁。无法对锁定的用户进行身份验证。 如果用户未被锁定,则返回true,否则返回false */ @Override public boolean isAccountNonLocked() { return true; } /** * 指示用户的凭证(密码)是否已过期。过期的凭证阻止身份验证 如果用户的凭证有效(即未过期),则返回true 如果不在有效(即过期),则返回false */ @Override public boolean isCredentialsNonExpired() { return true; } /** * 指示用户是启用还是禁用。无法对禁用的用户进行身份验证 如果启用了用户,则返回true,否则返回false */ @Override public boolean isEnabled() { return true; } }
package com.tszr.mapper; import java.util.List; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Mapper; import org.springframework.stereotype.Repository; import com.tszr.entity.Role; import com.tszr.entity.User; @Mapper @Repository public interface UserMapper { // @Select("select * from user where username = #{username}") User getUserByUsername(String username); List<Role> getRolesById(int id); // 添加用户 @Insert("insert into user(username, password) value(#{username}, #{password})") int insertUserInfo(User user); }
package com.tszr.services; import org.springframework.stereotype.Service; import com.tszr.entity.User; import com.tszr.mapper.UserMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.crypto.password.PasswordEncoder; @Service public class UserInfoService { @Autowired private UserMapper userMapper; @Autowired private PasswordEncoder passwordEncoder; public int insertUser(User userInfo) { // 加密密码 userInfo.setPassword(passwordEncoder.encode(userInfo.getPassword())); return userMapper.insertUserInfo(userInfo); } public User getUserInfo(String username) { return userMapper.getUserByUsername(username); } }
package com.tszr.services; import com.tszr.entity.User; import com.tszr.mapper.UserMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; public class CustomUserDetailsService implements UserDetailsService { @Autowired private UserInfoService userInfoService; @Autowired private UserMapper userMapper; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 通过用户名从数据库获取用户信息 User userInfo = userInfoService.getUserInfo(username); if (userInfo == null) { throw new UsernameNotFoundException("用户不存在"); } userInfo.setRoleList(userMapper.getRolesById(userInfo.getId())); return userInfo; } }
package com.tszr.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; import com.tszr.entity.User; import com.tszr.services.UserInfoService; @RestController public class UserController { @Autowired private UserInfoService userInfoService; // 添加 @PostMapping("/addUser") public int addUser(@RequestBody User userInfo) { return userInfoService.insertUser(userInfo); } @GetMapping("/getUser") public User getUser(@RequestParam String username) { return userInfoService.getUserInfo(username); } @PreAuthorize("hasAnyRole('user')") // 只能user角色才能访问该方法 @GetMapping("/user") public String user() { return "hello,user"; } @PreAuthorize("hasAnyRole('dba','admin')") // dba\admin角色可以访问该方法 @GetMapping("/db") public String dba() { return "hello,dba,admin"; } @PreAuthorize("hasAnyRole('admin')") // 只能admin角色才能访问该方法 @GetMapping("/admin") public String admin() { return "hello,admin"; } }
package com.tszr.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.http.HttpMethod; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 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.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import com.tszr.services.CustomUserDetailsService; @EnableWebSecurity // 是Spring Security用于启用Web安全的注解 @EnableGlobalMethodSecurity(prePostEnabled = true) // 开启方法级安全验证 public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private CustomUserDetailsService userDatailService; @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests().antMatchers(HttpMethod.POST, "/addUser").permitAll() // 允许post请求/add-user,而无需认证 .anyRequest().authenticated() // 所有请求都需要验证 .and().formLogin() // 使用默认的登录页面 .and().csrf().disable();// post请求要关闭csrf验证,不然访问报错;实际开发中开启,需要前端配合传递其他参数 } /** * 指定加密方式 */ @Bean public PasswordEncoder passwordEncoder() { // 使用BCrypt加密密码 return new BCryptPasswordEncoder(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // 从数据库读取的用户进行身份认证 auth.userDetailsService(userDatailService).passwordEncoder(passwordEncoder()); } }
package com.tszr; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
这篇关于基于数据库的认证的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-01后台管理开发学习:新手入门指南
- 2024-11-01后台管理系统开发学习:新手入门教程
- 2024-11-01后台开发学习:从入门到实践的简单教程
- 2024-11-01后台综合解决方案学习:从入门到初级实战教程
- 2024-11-01接口模块封装学习入门教程
- 2024-11-01请求动作封装学习:新手入门教程
- 2024-11-01登录鉴权入门:新手必读指南
- 2024-11-01动态面包屑入门:轻松掌握导航设计技巧
- 2024-11-01动态权限入门:新手必读指南
- 2024-11-01动态主题处理入门:新手必读指南