权限管理(一)
我用的是Spring Security权限管理,分为“认证”以及“验证”。
“认证”就是登陆,“验证”就是根据权限授予一定的操作,也就是权限。
Spring Security大致的过程就是这样
这个图片不是我做的,是另一个博主做的我觉得很不错。
原地址: https://blog.csdn.net/zhaoxichen_10/article/details/88713799.
(一)数据库设计
权限管理系统涉及到了三个数据库。
- hr数据库是存储账户信息
- menu是权限管理数据库
- role是角色数据库
(二)认证过程
认证过程就是用户根据hr中的username和password来登陆系统。
认证过程和menu类无关,不涉及menu类中的方法
1、Hr实体类
- 如果我们需要从数据库里面加载用户,那么我们在定义用户的时候就要实现一个接口UserDetails
- UserDetails相当于是一个规范,所有人都得实现它,这样的话以后我要获取用户名我就知道就是username,我要获取密码就是password
@Data
@Entity
@Table(name = "hr")
public class Hr implements UserDetails {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id ;
private String name;
private String phone;
private String telephone;
private String address;
private Boolean enabled;
private String username;
private String password;
private String userface;
private String remark;
@ManyToMany(cascade = {CascadeType.REFRESH},fetch=FetchType.EAGER)
@JoinTable(name = "hr_role", joinColumns = {@JoinColumn(name ="hrid" )}, inverseJoinColumns = { @JoinColumn(name = "rid") })
private List<Role> roles ;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Hr hr = (Hr) o;
return Objects.equals(username, hr.username);
}
@Override
public int hashCode() {
return Objects.hash(username);
}
//返回分配给用户的角色列表
@Override
@JsonIgnore
public Collection<? extends GrantedAuthority> getAuthorities() {
List<SimpleGrantedAuthority> authorities = new ArrayList<>(roles.size());
for (Role role : roles) {
authorities.add(new SimpleGrantedAuthority(role.getName()));
}
return authorities;
}
//账户是否过期
@Override
public boolean isAccountNonExpired() {
return true;
}
//账户是否锁定
@Override
public boolean isAccountNonLocked() {
return true;
}
//密码是否过期
@Override
public boolean isCredentialsNonExpired() {
return true;
}
//账户是否激活
@Override
public boolean isEnabled() {
return true;
}
2、关于JPA的多表联查
Hr和Role的关系是单向多对多
@ManyToMany(cascade = {CascadeType.REFRESH},fetch=FetchType.EAGER)
@JoinTable(name = "hr_role", joinColumns = {@JoinColumn(name ="hrid" )}, inverseJoinColumns = { @JoinColumn(name = "rid") })
private List<Role> roles ;
- 单向多对多就是生成一个中间表,多对多关联映射需要新增加一张表(hr_role)才完成基本映射
- JoinTable是中间表表名
- joinColumns指定中间表中关联自己ID的字段
- joinColumn是列名
- inverseJoinColumns表示中间表中关联对方ID的字段
hrid代表hr的id,rid表示Role中的id
3、HrService类
public interface HrRepository extends JpaRepository<Hr,Integer> {
public Hr findByUsername(String username);
}
创建HrService类,并实现UserDetailsService类,重写该类的loadUserByUsername方法
@Service
public class HrService implements UserDetailsService {
@Autowired
HrRepository hrRepository;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
Hr hr = hrRepository.findByUsername(s);
if (hr == null){
throw new UsernameNotFoundException("用户名不对");
}
return hr;
}
}
4、配置WebConfig类
webconfig类需要继承WebSecurityConfigurerAdapter类
@Configuration
public class webSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
HrService hrService;
//明文显示密码
@Bean
public CustomPasswordEncoder passwordEncoder() {
return new CustomPasswordEncoder();
}
//定义认证规则
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(hrService)
.passwordEncoder(new CustomPasswordEncoder());
}
由于spring5的内置加密方法是BCrypt,为了测试简单,我设置了明文加密
/**
* 明文显示密码
*/
public class CustomPasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence charSequence) {
return charSequence.toString();
}
@Override
public boolean matches(CharSequence charSequence, String s) {
return s.equals(charSequence.toString());
}
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
//剩下的其他请求都是登陆之后就能访问的
.anyRequest().authenticated()
//单表登陆
.and().formLogin()
//修改默认登陆的username
.usernameParameter("username")
//修改默认登陆的password
.passwordParameter("password")
//处理单表登陆的url路径
.loginProcessingUrl("/doLogin")
//默认看到的登录页面
.loginPage("/login")
//登陆成功的处理
.successHandler(new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication authentication) throws IOException, ServletException {
//如果登陆成功就返回一段json
resp.setContentType("application/json;charset=utf-8");
//这是往出写的
PrintWriter out = resp.getWriter();
//登陆成功的hr对象
Hr hr = (Hr) authentication.getPrincipal();
RespBean ok = RespBean.ok("登陆成功",hr);
//把hr写成字符串
String s = new ObjectMapper().writeValueAsString(ok);
//把字符串写出去
out.write(s);
out.flush();
out.close();
}
})
//登陆失败的处理
.failureHandler(new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse resp, AuthenticationException e) throws IOException, ServletException {
//如果登陆失败就返回一段json
resp.setContentType("application/json;charset=utf-8");
PrintWriter out = resp.getWriter();
RespBean respBean = RespBean.error("登陆失败!");
if (e instanceof LockedException){
respBean.setMsg("账户被锁定,请联系管理员!");
}else if (e instanceof CredentialsExpiredException){
respBean.setMsg("密码过期,请联系管理员!");
}else if (e instanceof AccountExpiredException){
respBean.setMsg("账户过期,请联系管理员!");
}else if (e instanceof DisabledException){
respBean.setMsg("账户被禁用,请联系管理员!");
}else if (e instanceof BadCredentialsException){
respBean.setMsg("用户名或者密码输入错误,请联系管理员!");
}
out.write(new ObjectMapper().writeValueAsString(respBean));
out.flush();
out.close();
}
})
//跟登陆相关的接口就能直接访问
.permitAll()
.and()
.logout()
//注销成功后的回调
.logoutSuccessHandler(new LogoutSuccessHandler() {
@Override
public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
httpServletResponse.setContentType("application/json;charset=utf-8");
PrintWriter out = httpServletResponse.getWriter();
out.write(new ObjectMapper().writeValueAsString(RespBean.ok("注销成功!")));
out.flush();
out.close();
}
})
.permitAll()
.and()
//关闭csrf攻击
.csrf().disable();
}
5、postman测试
到这里认证就完成了,接下来就是权限了。