五、密码修改
UserInfoMapper.java类中添加更新用户密码操作:
@Mapper @Repository public interface UserMapper { ... @Update("update user set password = #{newPwd} where username = #{username}") int updatePwd(String username, String newPwd); } UserInfoService.java类中添加更新密码的操作方法: @Service public class UserInfoService { ... public int updatePwd(String oldPwd, String newPwd) { // 获取当前登录用户信息(注意:没有密码的) UserDetails principal = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); String username = principal.getUsername(); // 通过用户名获取到用户信息(获取密码) UserInfo userInfo = userInfoMapper.getUserInfoByUsername(username); // 判断输入的旧密码是正确 if (passwordEncoder.matches(oldPwd, userInfo.getPassword())) { // 不要忘记加密新密码 return userInfoMapper.updatePwd(username, passwordEncoder.encode(newPwd)); } return 0; } }
HelloController.java类增加修改用户密码接口:
@RestController public class HelloController { @PutMapping("/updatePwd") public int updatePwd(@RequestBody Map<String, String> map){ return userInfoService.updatePwd(map.get("oldPwd"), map.get("newPwd")); } }
启动程序,因为需要登录,且修改密码请求方式为PUT请求,所以无法使用Postman发起请求,可以使用谷歌浏览器的插件Restlet Client:
先浏览器输入http://localhost:8080/login登录用户zeng/zeng123:
在Restlet Client中进行PUT更新请求:
这里更新后,需要重启后就可以使用新密码登录了。
5.用户角色多对多关系
上面的设置后,能基本实现了身份认证和角色授权了,但还是有一点不足:
我们前面用户表中,用户和角色是绑定一起,用户就只有一个角色了,但实际上,用户可能拥有多个角色,角色拥有多个用户,是多对多的关系,所以需要重新设置用户表和角色表。
创建普通项目运行
IDEA创建一个 Spring Initializr模块项目,名为b-database-manytomany-role,选择Spring Security和web依赖,其他按需选择Lombok、MySQL、JDBC和MyBatis。
先按照普通项目创建起来正常访问,在pom.xml中,先把Spring Security依赖注释
<!--<dependency>--> <!--<groupId>org.springframework.boot</groupId>--> <!--<artifactId>spring-boot-starter-security</artifactId>--> <!--</dependency>-->
新建表,主键id全部都是自增。
用户user2表
CREATE TABLE user2 ( uid int(11) NOT NULL AUTO_INCREMENT,
username varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci
NULL DEFAULT NULL, password varchar(255) CHARACTER SET utf8
COLLATE utf8_general_ci NULL DEFAULT NULL, PRIMARY KEY (uid) USING
BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 11 CHARACTER SET = utf8
COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
角色role2表,指定了2个角色
CREATE TABLE role2 ( rid int(11) NOT NULL AUTO_INCREMENT,
role varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL
DEFAULT NULL, PRIMARY KEY (rid) USING BTREE ) ENGINE = InnoDB
AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci
ROW_FORMAT = Dynamic;
用户角色关系user2_role2表
CREATE TABLE user2_role2 ( id int(11) NOT NULL AUTO_INCREMENT,
uid int(11) NULL DEFAULT NULL, rid int(11) NULL DEFAULT NULL,
PRIMARY KEY (id) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 10
CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
初始化数据:
– 添加1个用户 INSERT INTO user2 VALUES (1, ‘user’, ‘user123’);
– 添加2个角色 INSERT INTO role2 VALUES (1, ‘user’); INSERT INTO role2 VALUES (2, ‘admin’);
– 1个用户,拥有2个角色 INSERT INTO user2_role2 VALUES (1, 1, 1); INSERT INTO user2_role2 VALUES (2, 1, 2);
使用Navicat可视化表的数据结构为:
创建entity实体类
User类
@Data public class User { private Integer uid; private String username; private String password; }
Role类
@Data public class Role { private Integer rid; private String role; }
创建DTO类
因为用户和角色是多对多关系,需要在用户中含有角色的对象,角色中含有用户的对象,创建DTO类而不再entity类中添加,是因为entity类属性是和表字段一一对应的,一般不推荐在entity类中添加与表字段无关的属性。
新建dto包,在包下创建如下类:
UserDTO类
// 注意,多对多不要用@Data,因为ToString会相互调用,导致死循环
@Setter @Getter public class UserDTO extends User { private Set<Role> roles; }
RoleDTO类(目前用不到,可不建)
// 注意,多对多不要用@Data,因为ToString会相互调用,导致死循环
@Setter @Getter public class RoleDTO extends Role { private Set<User> users; }
添加UserMapper.java类
@Mapper @Repository public interface UserMapper { // 查询用户 UserDTO selectUserByUsername(@Param("username") String username); }
UserMapper.xml。因为需要关联查询,所有使用xml方式
<?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.example.bdatabasemanytomanyrole.mapper.UserMapper"> <resultMap id="userRoleMap" type="com.example.bdatabasemanytomanyrole.dto.UserDTO"> <id property="uid" column="uid"/> <result property="username" column="username"/> <result property="password" column="password"/> <collection property="roles" ofType="com.example.bdatabasemanytomanyrole.dto.RoleDTO"> <id property="rid" column="rid"/> <result property="role" column="role"></result> </collection> </resultMap> <select id="selectUserByUsername" resultMap="userRoleMap"> select user2.uid, user2.username, user2.password, role2.rid, role2.role from user2, user2_role2, role2 where user2.username=#{username} and user2.uid = user2_role2.uid and user2_role2.rid = role2.rid </select> </mapper>
UserService.java
@Service public class UserService { @Autowired private UserMapper userMapper; public UserDTO getUser(String username){ return userMapper.selectUserByUsername(username); } }
UserController.java
@RestController public class UserController { @Autowired private UserService userService; @GetMapping("/get-user") public UserDTO getUser(@RequestParam String username){ return userService.getUser(username); } }
上面完成后,启动项目,访问localhost:8080/get-user?username=user,查询到的用户信息为:
引入Spring Security安全验证
把pom.xml中的Spring Security依赖注释去掉:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
此时,再重新启动程序,访问localhost:8080/get-user?username=user时,会跳转到登录页面。此时默认的登录用户名为user,密码在启动时打印在控制台。
从数据库中获取用户、密码进行登录:
添加MyUserDetailsService.java,实现UserDetailsService,重写loadUserByUsername方法:
@Component public class MyUserDetailsService implements UserDetailsService { @Autowired private UserService userService; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { UserDTO user = userService.getUser(username); if (user == null) { throw new UsernameNotFoundException("用户不存在"); } // 添加用户拥有的多个角色 List<GrantedAuthority> grantedAuthorities = new ArrayList<>(); Set<Role> roles = user.getRoles(); for (Role role : roles) { grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_" + role.getRole())); } return new User( user.getUsername(), // 数据库中密码没加密,需加密 new BCryptPasswordEncoder().encode(user.getPassword()), grantedAuthorities ); } }
添加WebSecurityConfig.java,继承WebSecurityConfigurerAdapter,重写configure(AuthenticationManagerBuilder auth)方法:
@EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private MyUserDetailsService userDetailsService; @Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth .userDetailsService(userDatailService) .passwordEncoder(passwordEncoder()); } }
重新启动,可以使用user/user123登录了。
查看登录用户信息
要查看登录用户信息,我们可以在UserController中添加方法:
@RestController public class UserController { @Autowired private UserService userService; @GetMapping("/get-user") public UserDTO getUser(@RequestParam String username){ return userService.getUser(username); } /** * 查看登录用户信息 */ @GetMapping("/get-auth") public Authentication getAuth(){ return SecurityContextHolder.getContext().getAuthentication(); } }
重新启动登录后,访问localhost:8080/get-auth,返回:
以上就是本篇文章的有关内容
骚等别划走,恰饭时间~