一、实现UserDetails接口
package com.example.securityzimug.config.auth; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import java.util.Collection; public class MyUserDetails implements UserDetails { String password; //密码 String username; //用户名 boolean accountNonExpired; //是否没过期 boolean accountNonLocked; //是否没被锁定 boolean credentialsNonExpired; //是否没过期 boolean enabled; //账号是否可用 Collection<? extends GrantedAuthority> authorities; //用户的权限集合 @Override public Collection<? extends GrantedAuthority> getAuthorities() { return authorities; } @Override public String getPassword() { return password; } @Override public String getUsername() { return username; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return enabled; } public void setPassword(String password) { this.password = password; } public void setUsername(String username) { this.username = username; } public void setAccountNonExpired(boolean accountNonExpired) { this.accountNonExpired = accountNonExpired; } public void setAccountNonLocked(boolean accountNonLocked) { this.accountNonLocked = accountNonLocked; } public void setCredentialsNonExpired(boolean credentialsNonExpired) { this.credentialsNonExpired = credentialsNonExpired; } public void setEnabled(boolean enabled) { this.enabled = enabled; } public void setAuthorities(Collection<? extends GrantedAuthority> authorities) { this.authorities = authorities; } }
二、编写mapper
在编写之前需要导入依赖。
<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> </dependency>
package com.example.securityzimug.config.auth; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import java.util.List; @Mapper public interface MyUserDetailsServiceMapper { //根据userID查询用户信息 @Select("SELECT username,password,enabled\n" + "FROM sys_user u\n" + "WHERE u.username = #{userId} or u.phone = #{userId}") MyUserDetails findByUserName(@Param("userId") String userId); //根据userID查询用户角色列表 @Select("SELECT role_code\n" + "FROM sys_role r\n" + "LEFT JOIN sys_user_role ur ON r.id = ur.role_id\n" + "LEFT JOIN sys_user u ON u.id = ur.user_id\n" + "WHERE u.username = #{userId} or u.phone = #{userId}") List<String> findRoleByUserName(@Param("userId") String userId); //根据用户角色查询用户权限 @Select({ "<script>", "SELECT url " , "FROM sys_menu m " , "LEFT JOIN sys_role_menu rm ON m.id = rm.menu_id " , "LEFT JOIN sys_role r ON r.id = rm.role_id ", "WHERE r.role_code IN ", "<foreach collection='roleCodes' item='roleCode' open='(' separator=',' close=')'>", "#{roleCode}", "</foreach>", "</script>" }) List<String> findAuthorityByRoleCodes(@Param("roleCodes") List<String> roleCodes); }
三、实现UserDetailsService接口
package com.example.securityzimug.config.auth; import org.springframework.security.core.authority.AuthorityUtils; 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.Component; import javax.annotation.Resource; import java.util.List; import java.util.stream.Collectors; /** * @author baikunlong * @date 2020/9/22 23:14 */ @Component public class MyUserDetailsService implements UserDetailsService { @Resource private MyUserDetailsServiceMapper myUserDetailsServiceMapper; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { MyUserDetails myUserDetails = myUserDetailsServiceMapper.findByUserName(username); //角色集合 List<String> roleList = myUserDetailsServiceMapper.findRoleByUserName(username); //资源权限集合 List<String> authorities = myUserDetailsServiceMapper.findAuthorityByRoleCodes(roleList); //角色是特殊的权限,前缀规定为ROLE_ roleList=roleList.stream().map(role->"ROLE_"+role).collect(Collectors.toList()); authorities.addAll(roleList); myUserDetails.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList(String.join(",",authorities))); return myUserDetails; } }
四、修改配置类
注意这里的UserDetailsService一定要使用注入方式,我一开始手动new的,这会导致mapper为空,在service那排错了很久,后来才发现是这边的问题。
五、修改配置文件
spring: security: # 用数据库后,内存模式的就先注释掉吧 # user: # name: admin # password: admin loginType: JSON datasource: url: jdbc:mysql://localhost:3306/study_springboot_05_security_zimug?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai username: root password: '123456' driver-class-name: com.mysql.cj.jdbc.Driver #logging: # level: # root: debug
需要注意的是,配置文件如果是yml文件类型的,密码一定要加单引号或者双引号,不然连接不了数据库的,properties不存在这个问题。
六、动态鉴权
在一开始我们的鉴权都是写的静态的,当有新需求时可能会更改代码,所以需要动态的鉴权。
编写一个类来鉴权。
package com.example.securityzimug.config.auth; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import java.util.List; @Component("rabcService") public class MyRBACService { /** * 判断某用户是否具有该request资源的访问权限 */ public boolean hasPermission(HttpServletRequest request, Authentication authentication){ //这个就是UserDetails Object principal = authentication.getPrincipal(); if(principal instanceof UserDetails){ UserDetails userDetails = ((UserDetails)principal); //这里的请求地址就是资源权限名 List<GrantedAuthority> authorityList = AuthorityUtils.commaSeparatedStringToAuthorityList(request.getRequestURI()); //判断当前用户是否有这个资源权限 return userDetails.getAuthorities().contains(authorityList.get(0)); } return false; } }
然后在配置类里更改下权限配置: