用户认证
认证是指,用户是否在本系统,以及账号信息是否符合设定的预期。
查询用户(UserDetailsService)
实现UserDetailsService 接口,根据用户名称,查询用户信息。可实现自定义查询用户,并设置用户的角色信息。
实现了该接口,需要到配置对应的密码加密类 已实现对用户密码的校验。因前端输入的密码是未加密,而数据库保存的密码是已加密的。
@Service
public class CustomerUserDetailsService implements UserDetailsService {
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
CustomerUserDetails userDetails = new CustomerUserDetails();
userDetails.setUsername("user");
userDetails.setPassword(passwordEncoder.encode("123123"));
userDetails.setEnable(true);
userDetails.setAuthorities(Collections.emptySet());
userDetails.setAccountNonExpired(true);
userDetails.setAccountNonLocked(true);
userDetails.setCredentialsNonExpired(true);
return userDetails;
}
private Collection<? extends GrantedAuthority> loadRoleByUsername(String username){
Collection<SimpleGrantedAuthority> collection = new HashSet<>();
if("admin".equals(username)) {
collection.add(new SimpleGrantedAuthority("ADMIN"));
}else {
collection.add(new SimpleGrantedAuthority(username));
}
return collection;
}
private void loadGroupRoleByUsername(String username){
}
}
用户信息(UserDetails)
只需要实现UserDetails接口即可,自定义参数不需要和接口调用类保持一致(set/get)但最好保持一致,该接口是UserDetailsService接口中返回参数。
public class CustomerUserDetails implements UserDetails {
private String password;
private String username;
private Collection<? extends GrantedAuthority> authorities;
private boolean enable;
private boolean credentialsNonExpired;
private boolean accountNonExpired;
private boolean accountNonLocked;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return this.authorities;
}
@Override
public String getPassword() {
return this.password;
}
@Override
public String getUsername() {
return this.username;
}
@Override
public boolean isAccountNonExpired() {
return this.accountNonExpired;
}
@Override
public boolean isAccountNonLocked() {
return this.accountNonLocked;
}
@Override
public boolean isCredentialsNonExpired() {
return this.credentialsNonExpired;
}
@Override
public boolean isEnabled() {
return this.enable;
}
public void setPassword(String password) {
this.password = password;
}
public void setUsername(String username) {
this.username = username;
}
public void setAuthorities(Collection<? extends GrantedAuthority> authorities) {
this.authorities = authorities;
}
public boolean isEnable() {
return enable;
}
public void setEnable(boolean enable) {
this.enable = enable;
}
public void setCredentialsNonExpired(boolean credentialsNonExpired) {
this.credentialsNonExpired = credentialsNonExpired;
}
public void setAccountNonExpired(boolean accountNonExpired) {
this.accountNonExpired = accountNonExpired;
}
public void setAccountNonLocked(boolean accountNonLocked) {
this.accountNonLocked = accountNonLocked;
}
}
安全配置(WebSecurityConfigurerAdapter)
因使用密码加密类,加密了密码,需要在配置中配置密码类。
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
资源认证
资源认证,一般是基于URL与角色的认证。 需要自己定义
安全数据元(FilterInvocationSecurityMetadataSource)
通过数据库定义,URL和角色的授权关系。在将数据加入的缓存中已减少多次读取的问题。
设计的时候:
- 如果只是需要登录即可访问的资源,可以设计一个角色,在用户注册时候就授予这个角色。
- 如果需要指定角色,则需要URL和角色一一对应。
public class CustomerFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
protected final Log logger = LogFactory.getLog(getClass());
private AntPathMatcher antPathMatcher = new AntPathMatcher();
public CustomerFilterInvocationSecurityMetadataSource(){
}
public Collection<ConfigAttribute> getAllConfigAttributes() {
Set<ConfigAttribute> allAttributes = new HashSet<>();
logger.info("getAllConfigAttributes");
return allAttributes;
}
public Collection<ConfigAttribute> getAttributes(Object object) {
logger.info("getAttributes");
String url = ((FilterInvocation) object).getRequestUrl();
int index = url.indexOf("?");
if(index != -1){
url = url.substring(0,index);
}
Collection<ConfigAttribute> collection = getRoleByUrl(url);
if(collection.isEmpty()){
}
return collection;
}
public Collection<ConfigAttribute> getRoleByUrl(String url) {
Collection<ConfigAttribute> collection = new HashSet<>();
Map<String,Collection<ConfigAttribute>> map = new HashMap<>();
Iterator<String> iterator = map.keySet().iterator();
collection.add(new SecurityConfig("ADMIN"));
while (iterator.hasNext()){
String matchUrl = iterator.next();
if(this.antPathMatcher.match(matchUrl,url)){
Collection<ConfigAttribute> matchCollection = map.get(matchUrl);
collection.addAll(matchCollection);
}
}
return collection;
}
@Override
public boolean supports(Class<?> clazz) {
return FilterInvocation.class.isAssignableFrom(clazz);
}
}
访问决策管理
在获取URL对应的角色时,会将用户的角色和获取的角色进行比较,查看用户是否有权限访问。
public class CustomerAccessDecisionManager implements AccessDecisionManager {
protected final Log logger = LogFactory.getLog(getClass());
@Override
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
Iterator<ConfigAttribute> iterator = configAttributes.iterator();
logger.info("decide");
while (iterator.hasNext()){
ConfigAttribute attribute = iterator.next();
for(GrantedAuthority ga : authentication.getAuthorities()){
if(attribute.getAttribute().equals(ga.getAuthority())){
return;
}
}
}
throw new AccessDeniedException("Access reject!");
}
@Override
public boolean supports(ConfigAttribute attribute) {
return true;
}
@Override
public boolean supports(Class<?> clazz) {
return true;
}
}
安全拦截器(FilterSecurityInterceptor)
需要继承FilterSecurityInterceptor类,在这里设置安全元数据、访问决策管理。如果不需要的访问,可以通过重写进行覆盖父类的方法。
@Component
public class CustomerFilterSecurityInterceptor extends FilterSecurityInterceptor {
@PostConstruct
public void init(){
logger.info("init info");
super.setSecurityMetadataSource(new CustomerFilterInvocationSecurityMetadataSource());
super.setAccessDecisionManager(new CustomerAccessDecisionManager());
}
@Override
public void invoke(FilterInvocation fi) throws IOException, ServletException {
logger.info("invoke");
InterceptorStatusToken token = beforeInvocation(fi);
try {
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
}
finally {
super.finallyInvocation(token);
}
super.afterInvocation(token, null);
}
}
安全配置(WebSecurityConfigurerAdapter)
在用户认证,安全配置类中加入,安全拦截器功能
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
protected final Log logger = LogFactory.getLog(getClass());
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Override
public void configure(WebSecurity web) throws Exception {
logger.info("configure(WebSecurity web)");
web.securityInterceptor(new CustomerFilterSecurityInterceptor());
}
}
|