用户认证
认证是指,用户是否在本系统,以及账号信息是否符合设定的预期。
查询用户(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;
}
/**
* 角色信息可以参考SimpleGrantedAuthority类
* 根据用户名, 返回用户组角色
*/
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和角色一一对应。
/**
* 访问决策需要访问的资源
*/
@Component
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对应的角色时,会将用户的角色和获取的角色进行比较,查看用户是否有权限访问。
/**
* 访问决策管理
* 讲通过URL获取到对应授权的角色,和当前用户拥有角色进行比较。 查看是否拥有请求当前资源权限
*/
@Component
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());
}
*/
@Autowired
@Override
//自动注入自定义安全元数据
public void setSecurityMetadataSource(FilterInvocationSecurityMetadataSource source){
super.setSecurityMetadataSource(source);
}
@Autowired
@Override
//自动注入自定义访问决策
public void setAccessDecisionManager(AccessDecisionManager accessDecisionManager){
super.setAccessDecisionManager(accessDecisionManager);
}
/**
* 这一步可以不重写,使用系统自带的方法,如果不需要或是没有配置那些功能,则可以重写为自己想要的功能
*/
@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());
web.securityInterceptor(this.getApplicationContext().getBean(FilterSecurityInterceptor.class));
}
}
版权声明:本文为swg321321原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。