一、前言
这里以oauth2协议+springSecurity框架的账户密码和微信扫码登陆为例。一方面是密码登录改写UserDeatilService的实现方法loadUserByUsername();
另一方面是改写类DaoAuthenticationProvider的方法additionalAuthenticationChecks()。
二、区别
2.1
UserDeatilService的实现方法loadUserByUsername()改写 由于UserDeatilService在第三方包中是已经注入了 改写了方法也会自动生效【当调用这个方法的时候】
2.2
改写类DaoAuthenticationProvider的方法additionalAuthenticationChecks(),由于类的改写是继承的方式,自己改写的类,第三方包是没有办法获取的,只在本地生效,因此:1、需要自己注入到bean中,这样第三方包才能在ico中获取重写的类 2、需要在调用这个类的地方换成自己写的类 不然还是默认使用第三方的类
代码示例:
改写UserDeatilService的实现方法loadUserByUsername()
package com.xuecheng.ucenter.service.impl;
import com.alibaba.fastjson.JSON;
import com.xuecheng.ucenter.mapper.XcUserMapper;
import com.xuecheng.ucenter.model.dto.AuthParamsDto;
import com.xuecheng.ucenter.model.dto.XcUserExt;
import com.xuecheng.ucenter.service.AuthService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @author Mr.Lan
* @version 1.0
* @ClassName UserServiceImpl$
* @description TODO
* @date 2024/4/13 21:00
**/
@Slf4j
@Service
public class UserServiceImpl implements UserDetailsService {
@Autowired
XcUserMapper xcUserMapper;
@Autowired
ApplicationContext applicationContext;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { //username和password的校验目前不清楚逻辑
// 这里是生成令牌 用authParamsDto接口请求参数
AuthParamsDto authParamsDto = null;
try {
//将认证参数转为AuthParamsDto类型
authParamsDto = JSON.parseObject(s, AuthParamsDto.class);
} catch (Exception e) {
log.info("认证请求不符合项目要求:{}",s);
throw new RuntimeException("认证请求数据格式不对");
}
/**
* 原来/auth/oauth/token?client_id=XcWebApp&client_secret=XcWebApp&grant_type=password&username=t1&password=111111
* 其中String s 获取的变量是username
* 现在解析json传那么传的username也是json串 后面服务解析的也是变量userName(只是一个名字具体定义看自己)
*userName= json串
*
**
* @author Mr.Lan
* @Description TODO
* @Date 17:20
* @Param [s]
* @return org.springframework.security.core.userdetails.UserDetails
**/
//获取认证类型
String authType = authParamsDto.getAuthType();
//根据认证类型从容器中取出指定的bean
String beanName = authType + "_authservice";
AuthService authService = applicationContext.getBean(beanName, AuthService.class);
//调用统一的excute方法完成认证
XcUserExt user = authService.execute(authParamsDto);
//封装用户信息XcUserExt成userDetails
// XcUser user = xcUserMapper.selectOne(new LambdaQueryWrapper<XcUser>().eq(XcUser::getUsername, authParamsDto.getUsername()));
// if (user == null) {
// //返回空表示用户不存在
// return null;
// }
// //取出数据库存储的正确密码
// String password = user.getPassword();
//用户权限,如果不加报Cannot pass a null GrantedAuthority collection
return getUserDetails(user);
}
private UserDetails getUserDetails(XcUserExt user) {
//获取用户权限
List<String> menuList = xcUserMapper.getMenuList(user.getId());
//List转数组
String[] Premissions = menuList.toArray(new String[0]);
// String[] authorities = {"test"};
String[] authorities = Premissions;
//创建UserDetails对象,权限信息待实现授权功能时再向UserDetail中加入
//withUsername()是存储一个变量可以是用户名也可以是json串
// user.setPermissions(Arrays.stream(authorities).collect(Collectors.toList()));
//已将校验密码了 密码不必存 安全性考虑
user.setPassword(null);
String userJson = JSON.toJSONString(user);
//这里username、password是token中的内容password默认在additionalAuthenticationChecks中校验 但是additionalAuthenticationChecks已将被覆盖了
//所以password已经失去校验功能 随便填
UserDetails userDetails = User.withUsername(userJson).password("1").authorities(authorities).build();
return userDetails;
}
}
改写类DaoAuthenticationProvider的方法additionalAuthenticationChecks()并注入、调用的地方更新
注入:
@Component
public class DaoAuthenticationProviderCustom extends DaoAuthenticationProvider {
@Autowired
public void setUserDetailsService(UserDetailsService userDetailsService) {
super.setUserDetailsService(userDetailsService);
}
//取消校验
@Override
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
}
}
更新:
package com.xuecheng.auth.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* @author Mr.M
* @version 1.0
* @description 安全管理配置
* @date 2022/9/26 20:53
*/
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
DaoAuthenticationProviderCustom daoAuthenticationProviderCustom;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(daoAuthenticationProviderCustom);
}
// 配置用户信息服务
// @Bean
// public UserDetailsService userDetailsService() {
// //这里配置用户信息,这里暂时使用这种方式将用户存储在内存中
// InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
// manager.createUser(User.withUsername("zhangsan").password("123").authorities("p1").build());
// manager.createUser(User.withUsername("lisi").password("456").authorities("p2").build());
// return manager;
// }
@Bean
public PasswordEncoder passwordEncoder() {
// //密码为明文方式
// return NoOpPasswordEncoder.getInstance();
//密码为
return new BCryptPasswordEncoder();
}
//配置安全拦截机制
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/r/**").authenticated()//访问/r开始的请求需要认证通过
.anyRequest().permitAll()//其它请求全部放行
.and()
.formLogin().successForwardUrl("/login-success");//登录成功跳转到/login-success
}
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
三、总结
很多技术栈没必要将代码完完整整的敲出来,搞清楚原理,然后会CV使用以及派排查出现的问题就够了。面试和工作都是如此,没人问你怎么做出来的细节 ,只关心你懂不懂,能不能再现有的资源整合下作出来。