- 只要加入springsecurity依赖,所有接口都会被保护,需要登录、数据库中包含Hr(用户表)、role(角色表)、hr_role(用户-角色表)、menu(资源表)、menu_role(资源-角色表)。
- 用户角色实体类:Hr 要实现 UserDetails接口
public class Hr implements UserDetails {
private Long id;
private String name;
private String phone;
private String telephone;
private String address;
private boolean enabled;
private String username;
private String password;
private String remark;
private List<Role> roles;
private String userface;
@Override
public boolean isEnabled() {
return enabled;
}
@Override
public String getUsername() {
return username;
}
@JsonIgnore
@Override
public boolean isAccountNonExpired() {
return true;
}
@JsonIgnore
@Override
public boolean isAccountNonLocked() {
return true;
}
@JsonIgnore
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@JsonIgnore
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
//存储用户所具备的角色,存到----->authorities
List<GrantedAuthority> authorities = new ArrayList<>();
for (Role role : roles) {
authorities.add(new SimpleGrantedAuthority(role.getName()));
}
return authorities;
}
@JsonIgnore
@Override
public String getPassword() {
return password;
}
public String getUserface() {
return userface;
}
public void setUserface(String userface) {
this.userface = userface;
}
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getTelephone() {
return telephone;
}
public void setTelephone(String telephone) {
this.telephone = telephone;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
}
- service层 HrService实现UserDetailsService
@Service
@Transactional
public class HrService implements UserDetailsService {
@Autowired
HrMapper hrMapper;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
Hr hr = hrMapper.loadUserByUsername(s);
if (hr == null) {
throw new UsernameNotFoundException("用户名不对");
}
return hr;
}
}
- 登录逻辑:用户登录-----》调用loadUserByUsername(String s)(springsecurity自动把用户名放到s中),返回hr对象,其中包含用户所有角色存储到getAuthorities()这个函数中,并返回用户角色对象authorities。
- 根据请求地址,分析出所需要的角色CustomMetadataSource------->UrlAccessDecisionManager
@Component
public class CustomMetadataSource implements FilterInvocationSecurityMetadataSource {
@Autowired
MenuService menuService;
AntPathMatcher antPathMatcher = new AntPathMatcher();
@Override
public Collection<ConfigAttribute> getAttributes(Object o) {
String requestUrl = ((FilterInvocation) o).getRequestUrl();
List<Menu> allMenu = menuService.getAllMenu();
for (Menu menu : allMenu) {
if (antPathMatcher.match(menu.getUrl(), requestUrl)
&&menu.getRoles().size()>0) {
List<Role> roles = menu.getRoles();
int size = roles.size();
String[] values = new String[size];
for (int i = 0; i < size; i++) {
values[i] = roles.get(i).getName();
}
return SecurityConfig.createList(values);//解析到角色配置到springsecurity当中与hasRole()一个道理。
}
}
//没有匹配上的资源,都是登录访问
return SecurityConfig.createList("ROLE_LOGIN");
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
@Override
public boolean supports(Class<?> aClass) {
return FilterInvocation.class.isAssignableFrom(aClass);
}
}
- 需要角色与所拥有角色进行比对
@Component
public class UrlAccessDecisionManager implements AccessDecisionManager {
@Override
public void decide(Authentication auth, Object o, Collection<ConfigAttribute> cas){
Iterator<ConfigAttribute> iterator = cas.iterator();
while (iterator.hasNext()) {
ConfigAttribute ca = iterator.next();
//当前请求需要的权限
String needRole = ca.getAttribute();
if ("ROLE_LOGIN".equals(needRole)) {
if (auth instanceof AnonymousAuthenticationToken) {
throw new BadCredentialsException("未登录");
} else
return;
}
//当前用户所具有的权限
Collection<? extends GrantedAuthority> authorities = auth.getAuthorities();
for (GrantedAuthority authority : authorities) {
if (authority.getAuthority().equals(needRole)) {
return;
}
}
}
throw new AccessDeniedException("权限不足!");
}
@Override
public boolean supports(ConfigAttribute configAttribute) {
return true;
}
@Override
public boolean supports(Class<?> aClass) {
return true;
}
}
- 配置springsecurity,把CustomMetadataSource metadataSource、UrlAccessDecisionManager urlAccessDecisionManager;配置到springsecurity
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
HrService hrService;
@Autowired
CustomMetadataSource metadataSource;
@Autowired
UrlAccessDecisionManager urlAccessDecisionManager;
@Autowired
AuthenticationAccessDeniedHandler deniedHandler;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(hrService)
.passwordEncoder(new BCryptPasswordEncoder());//hrService就是自己写的类, 这个类的作用就是
// 去获取用户信息,比如从数据库中获取。 这样的话,AuthenticationManager在认证用户身份信息的时候,
// 就回从中获取用户身份,和从http中拿的用户身份做对比。
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/index.html", "/static/**", "/login_p", "/favicon.ico");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
@Override
public <O extends FilterSecurityInterceptor> O postProcess(O o) {
o.setSecurityMetadataSource(metadataSource);
o.setAccessDecisionManager(urlAccessDecisionManager);
return o;
}
})
.and()
.formLogin().loginPage("/login_p").loginProcessingUrl("/login")
.usernameParameter("username").passwordParameter("password")
.failureHandler(new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest req,
HttpServletResponse resp,
AuthenticationException e) throws IOException {
resp.setContentType("application/json;charset=utf-8");
RespBean respBean = null;
if (e instanceof BadCredentialsException ||
e instanceof UsernameNotFoundException) {
respBean = RespBean.error("账户名或者密码输入错误!");
} else if (e instanceof LockedException) {
respBean = RespBean.error("账户被锁定,请联系管理员!");
} else if (e instanceof CredentialsExpiredException) {
respBean = RespBean.error("密码过期,请联系管理员!");
} else if (e instanceof AccountExpiredException) {
respBean = RespBean.error("账户过期,请联系管理员!");
} else if (e instanceof DisabledException) {
respBean = RespBean.error("账户被禁用,请联系管理员!");
} else {
respBean = RespBean.error("登录失败!");
}
resp.setStatus(401);
ObjectMapper om = new ObjectMapper();
PrintWriter out = resp.getWriter();
out.write(om.writeValueAsString(respBean));
out.flush();
out.close();
}
})
.successHandler(new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest req,
HttpServletResponse resp,
Authentication auth) throws IOException {
resp.setContentType("application/json;charset=utf-8");
RespBean respBean = RespBean.ok("登录成功!", HrUtils.getCurrentHr());
ObjectMapper om = new ObjectMapper();
PrintWriter out = resp.getWriter();
out.write(om.writeValueAsString(respBean));
out.flush();
out.close();
}
})
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessHandler(new LogoutSuccessHandler() {
@Override
public void onLogoutSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication authentication) throws IOException, ServletException {
resp.setContentType("application/json;charset=utf-8");
RespBean respBean = RespBean.ok("注销成功!");
ObjectMapper om = new ObjectMapper();
PrintWriter out = resp.getWriter();
out.write(om.writeValueAsString(respBean));
out.flush();
out.close();
}
})
.permitAll()
.and().csrf().disable()
.exceptionHandling().accessDeniedHandler(deniedHandler)
.and();
}
}
网友评论