从UserDetails的源码中我们了解到,UserDetails其实就是一个包含了User信息的类,其中包含了用户名、密码、角色及账号状态等信息。
**利用以上的两个核心API,我们就可以进行基于自定义数据库模型的认证授权工作了,**因为不管我们项目中关于认证、授权的数据库结构如何变化,只要我们构造出一个UserDetails类,然后利用UserDetailsService进行用户信息的加载就可以了。
**二. 基于自定义数据库模型实现授权**
**1. 创建数据库**
在开始今天的代码之前,请先跟着壹哥来创建一个数据库,并在该库里创建用户角色表,建表脚本如下:
CREATE TABLE users
(id
bigint(20) NOT NULL AUTO_INCREMENT,
username
varchar(50) NOT NULL,
password
varchar(60) NOT NULL,
enable
tinyint(4) NOT NULL DEFAULT 1
,
roles
text character set utf8,
PRIMARY KEY (id
),
KEY username
(username
)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
该表中包含了用户名、密码、角色、可用状态等信息,然后我们在该表中添加一些测试数据,如下图所示:
![图片](https://img-blog.csdnimg.cn/img_convert/804e7c4ca58b2deddf0dfdd505ac0c98.png)
**2. 创建项目**
准备好了数据库之后,**我们就继续在之前项目的基础之上,创建一个新的model模块,**其基本配置信息和之前一样,具体创建过程略,请参考之前的章节进行项目创建!
**3. 创建User实体类**
/**
* 用户操作实体类
*/
public class User implements UserDetails {
private Long id;
private String username;
private String password;
private String roles;
private boolean enable;
private List authorities;
public String getRoles() {
return roles;
}
public void setRoles(String roles) {
this.roles = roles;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public boolean isEnable() {
return enable;
}
public void setEnable(boolean enable) {
this.enable = enable;
}
@Override
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return this.enable;
}
public void setAuthorities(List authorities) {
this.authorities = authorities;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return this.authorities;
}
@Override
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public boolean equals(Object obj) {
return obj instanceof User?this.username.equals(((User)obj).username):false;
}
@Override
public int hashCode() {
return this.username.hashCode();
}
}
在刚才的模块里,我们先创建一个User实体类,这个用户实体类需要实现 **UserDetails** 接口,并实现接口中的方法。
**核心方法介绍:**
* accountNonExpired、accountNonLocked、credentialsNonExpired、enabled 这四个属性分别用来描述用户的状态,表示账户是否没有过期、账户是否没有被锁定、密码是否没有过期、以及账户是否可用;
* roles 属性表示用户的角色;
* getAuthorities 方法返回用户的角色信息,一个用户可能会有多个角色,所以这里返回值是一个集合类型,我们在这个方法中把自己的 Role 角色稍微转化一下即可。
**4. 定义Mapper接口**
数据模型准备好之后,我们再来定义一个 UserMapper接口,这里我们利用Mybatis进行具体的数据库查询,直接利用注解的方式实现即可。
/**
* @Mapper注解,可带可不带,因为有MapperScan扫描.
*/
@Mapper
public interface UserMapper {
@Select(“SELECT * FROM users WHERE username=#{username}”)
User findByUserName(@Param(“username”) String username);
}
**5. 实现UserDetailsService接口**
接在在service层,定义一个UserDetailsService子类,**实现UserDetailsService接口**,然后实现该接口中的loadUserByUsername()方法。这个方法的参数是用户在登录的时候传入的用户名,然后根据用户名去查询用户信息(查出来之后,系统会自动进行密码比对)。
@Service
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 从数据库尝试读取该用户
User user = userMapper.findByUserName(username);
// 用户不存在,抛出异常
if (user == null) {
throw new UsernameNotFoundException(“用户不存在”);
}
// 将数据库形式的roles解析为UserDetails的权限集
// AuthorityUtils.commaSeparatedStringToAuthorityList是Spring Security
//提供的用于将逗号隔开的权限集字符串切割成可用权限对象列表的方法
// 当然也可以自己实现,如用分号来隔开等,参考generateAuthorities
user.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList(user.getRoles()));
return user;
}
}
主要要在实现方法中,利用user对象的setAuthorities()方法关联用户的所有角色信息,否则用户登录认证时就没有角色,认证也会失败。
**6. 配置SecurityConfig类**
编写完上面的UserDetailsService接口后,接下来我们创建SecurityConfig配置类,我们在configure方法中,关联配置自定义的UserService对象。
@EnableWebSecurity(debug = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(“/admin/“)
.hasRole(“ADMIN”)
.antMatchers(”/user/”)
.hasRole(“USER”)
.antMatchers(“/visitor/**”)
.permitAll()
.anyRequest()
.authenticated()
.and()
.formLogin()
.permitAll();
}
/在数据库中创建用户和角色*************/
@Autowired
private MyUserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//关联UserDetailsService对象
最后总结我的面试经验
2021年的金三银四一眨眼就到了,对于很多人来说是跳槽的好机会,大厂面试远没有我们想的那么困难,摆好心态,做好准备,你也可以的。
另外,面试中遇到不会的问题不妨尝试讲讲自己的思路,因为有些问题不是考察我们的编程能力,而是逻辑思维表达能力;最后平时要进行自我分析与评价,做好职业规划,不断摸索,提高自己的编程能力和抽象思维能力。
BAT面试经验
实战系列:Spring全家桶+Redis等
其他相关的电子书:源码+调优
面试真题:
g-rZJ86Rld-1714755350971)]
BAT面试经验
实战系列:Spring全家桶+Redis等
[外链图片转存中…(img-96iXb0vV-1714755350971)]
其他相关的电子书:源码+调优
[外链图片转存中…(img-MmeMKTgo-1714755350972)]
面试真题:
[外链图片转存中…(img-KYoKvpbp-1714755350972)]
[外链图片转存中…(img-DfIMBJrH-1714755350973)]