SpringSecurity+JWT认证
SpringSecurity+JWT认证
1.创建UserDetailsServiceImpl并继承UserDetailsService
代码如下
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserMapper userMapper;//用户查询Mapper
@Autowired
private MenuMapper menuMapper;//权限查询Mapper
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//查询用户信息
LambdaQueryWrapper<User> queryWrapper=new LambdaQueryWrapper<>();
queryWrapper.eq(User::getUserName,username);
User user = userMapper.selectOne(queryWrapper);
//如果没有查询到用户就抛出异常
if (Objects.isNull(user)){
throw new RuntimeException("用户名或密码错误");
}
//TODO 查询对应的权限信息
// List<String> list=new ArrayList<>(Arrays.asList("test","admin"));
List<String> list = menuMapper.selectPermsByUserId(user.getId());
//把数据封装成UserDetails返回
return new LoginUser(user,list);
}
}
代码二
@Service
@Slf4j
public class SecurityUserDetailsServiceImpl implements UserDetailsService {
@Autowired
private SysUserService sysUserService;
@Autowired
private SysMenService sysMenService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
SysUser sysUser = sysUserService.getByUserName(username);
if (sysUser==null){
throw new UsernameNotFoundException("该用户不处在");
}
List<String> userPermissions = sysMenService.queryPermissionByUserId(sysUser.getUserId());
List<SimpleGrantedAuthority> authorities=new ArrayList<>();
userPermissions.forEach(s -> {
SimpleGrantedAuthority authority = new SimpleGrantedAuthority(s);
authorities.add(authority);
});
MySercurityUser securiruUser=new MySercurityUser(sysUser);
securiruUser.setAuthorities(authorities);
return securiruUser;
}
}
2.创建继承UserDetails的类
代码如下
@Data
@NoArgsConstructor
public class LoginUser implements UserDetails {
private User user;
private List<String> permissions;
public LoginUser(User user, List<String> permissions) {
this.user = user;
this.permissions = permissions;
}
@JSONField(serialize = false)
private List<GrantedAuthority> authorities=null;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
if (authorities!=null){
return authorities;
}
//把permissions中String类型的权限信息封装成SimpleGrantedAuthority(实现类)对象
/*List<GrantedAuthority> authorities=new ArrayList<>();
for (String permission : permissions) {
SimpleGrantedAuthority authority = new SimpleGrantedAuthority(permission);
authorities.add(authority);
}*/
authorities = permissions.stream()
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
return authorities;
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUserName();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
代码二
public class MySercurityUser implements UserDetails {
private final SysUser sysUser;
//用于存储权限的List
private List<SimpleGrantedAuthority> authorities;
public MySercurityUser(SysUser sysUser){
this.sysUser=sysUser;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
//配置权限
return authorities;
/* GrantedAuthority grantedAuthority= () -> "null";
List<GrantedAuthority> list=new ArrayList<>();
list.add(grantedAuthority);
return list;*/
}
public void setAuthorities(List<SimpleGrantedAuthority> authorities) {
this.authorities = authorities;
}
@Override
public String getPassword() {
String mypassword=sysUser.getPassword();
sysUser.setPassword(null);
return mypassword;
}
@Override
public String getUsername() {
return this.sysUser.getUsername();
}
@Override
public boolean isAccountNonExpired() {//账号是否过期
return sysUser.getAccountNoExpired().equals(1);
}
@Override
public boolean isAccountNonLocked() {//账号是否未锁定
return sysUser.getAccountNoLocked().equals(1);
}
@Override
public boolean isCredentialsNonExpired() {//凭据是否未过期
return sysUser.getCredentialsNoExpired().equals(1);
}
@Override
public boolean isEnabled() {
return sysUser.getEnabled().equals(1);
}
}
3.创建登录控制器LoginController
代码如下
@RestController
public class LoginController {
@Autowired
private LoginService loginService;
@RequestMapping("/user/login")
public ResponseResult login(@RequestBody User user){
//登录
return loginService.login(user);
}
@RequestMapping("/user/logout")
public ResponseResult logout(){
return loginService.logout();
}
}
4.创建拦截器
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
@Autowired
private RedisCache redisCache;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
//获取token
String token = request.getHeader("token");
if (!StringUtils.hasText(token)) {
//放行
filterChain.doFilter(request, response);
return;
}
//解析token
String userid;
try {
Claims claims = JwtUtil.parseJWT(token);
userid = claims.getSubject();
} catch (Exception e) {
throw new RuntimeException("token非法");
}
//从redis中获取用户信息
String redisKey = "login:" + userid;
LoginUser loginUser = redisCache.getCacheObject(redisKey);
if (Objects.isNull(loginUser)){
throw new RuntimeException("用户未登录");
}
//存入SecurityContextHolder
//TODO 获取权限信息封装到Authentication中
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(loginUser,null,loginUser.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
//放行
filterChain.doFilter(request, response);
}
}
5.SecurityConfig配置类
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//创建BCryptPasswordEncoder注入容器
@Bean
public BCryptPasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Autowired
private JwtAuthenticationTokenFilter authenticationTokenFilter;
@Autowired
private AuthenticationEntryPointImpl authenticationEntryPoint;
@Autowired
private AccessDeniedHandlerImpl accessDeniedHandler;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
//关闭csrf
.csrf().disable()
//不通过Session获取SecurityContext
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
// 对于登录接口 允许匿名访问
.antMatchers("/user/login").anonymous()
.antMatchers("/testCors").hasAuthority("system:detp:list")
// 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated();
//添加过滤器
http.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
//配置异常处理器
http.exceptionHandling()
//配置认真失败处理器
.authenticationEntryPoint(authenticationEntryPoint)
.accessDeniedHandler(accessDeniedHandler);
//允许跨域请求
http.cors();
}
}
6.将字符串渲染到客户端工具类
public class WebUtils
{
/**
* 将字符串渲染到客户端
*
* @param response 渲染对象
* @param string 待渲染的字符串
* @return null
*/
public static void renderString(HttpServletResponse response, String string) {
try
{
response.setStatus(200);
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
response.getWriter().print(string);
}
catch (IOException e)
{
e.printStackTrace();
}
}
public static void setDownLoadHeader(String filename, ServletContext context, HttpServletResponse response) throws UnsupportedEncodingException {
String mimeType = context.getMimeType(filename);//获取文件的mime类型
response.setHeader("content-type",mimeType);
String fname= URLEncoder.encode(filename,"UTF-8");
response.setHeader("Content-disposition","attachment; filename="+fname);
// response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
// response.setCharacterEncoding("utf-8");
}
}
7.Redis工具栏
@SuppressWarnings(value = { "unchecked", "rawtypes" })
@Component
public class RedisCache
{
@Autowired
public RedisTemplate redisTemplate;
/**
* 缓存基本的对象,Integer、String、实体类等
*
* @param key 缓存的键值
* @param value 缓存的值
*/
public <T> void setCacheObject(final String key, final T value)
{
redisTemplate.opsForValue().set(key, value);
}
/**
* 缓存基本的对象,Integer、String、实体类等
*
* @param key 缓存的键值
* @param value 缓存的值
* @param timeout 时间
* @param timeUnit 时间颗粒度
*/
public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit)
{
redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
}
/**
* 设置有效时间
*
* @param key Redis键
* @param timeout 超时时间
* @return true=设置成功;false=设置失败
*/
public boolean expire(final String key, final long timeout)
{
return expire(key, timeout, TimeUnit.SECONDS);
}
/**
* 设置有效时间
*
* @param key Redis键
* @param timeout 超时时间
* @param unit 时间单位
* @return true=设置成功;false=设置失败
*/
public boolean expire(final String key, final long timeout, final TimeUnit unit)
{
return redisTemplate.expire(key, timeout, unit);
}
/**
* 获得缓存的基本对象。
*
* @param key 缓存键值
* @return 缓存键值对应的数据
*/
public <T> T getCacheObject(final String key)
{
ValueOperations<String, T> operation = redisTemplate.opsForValue();
return operation.get(key);
}
/**
* 删除单个对象
*
* @param key
*/
public boolean deleteObject(final String key)
{
return redisTemplate.delete(key);
}
/**
* 删除集合对象
*
* @param collection 多个对象
* @return
*/
public long deleteObject(final Collection collection)
{
return redisTemplate.delete(collection);
}
/**
* 缓存List数据
*
* @param key 缓存的键值
* @param dataList 待缓存的List数据
* @return 缓存的对象
*/
public <T> long setCacheList(final String key, final List<T> dataList)
{
Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
return count == null ? 0 : count;
}
/**
* 获得缓存的list对象
*
* @param key 缓存的键值
* @return 缓存键值对应的数据
*/
public <T> List<T> getCacheList(final String key)
{
return redisTemplate.opsForList().range(key, 0, -1);
}
/**
* 缓存Set
*
* @param key 缓存键值
* @param dataSet 缓存的数据
* @return 缓存数据的对象
*/
public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet)
{
BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
Iterator<T> it = dataSet.iterator();
while (it.hasNext())
{
setOperation.add(it.next());
}
return setOperation;
}
/**
* 获得缓存的set
*
* @param key
* @return
*/
public <T> Set<T> getCacheSet(final String key)
{
return redisTemplate.opsForSet().members(key);
}
/**
* 缓存Map
*
* @param key
* @param dataMap
*/
public <T> void setCacheMap(final String key, final Map<String, T> dataMap)
{
if (dataMap != null) {
redisTemplate.opsForHash().putAll(key, dataMap);
}
}
/**
* 获得缓存的Map
*
* @param key
* @return
*/
public <T> Map<String, T> getCacheMap(final String key)
{
return redisTemplate.opsForHash().entries(key);
}
/**
* 往Hash中存入数据
*
* @param key Redis键
* @param hKey Hash键
* @param value 值
*/
public <T> void setCacheMapValue(final String key, final String hKey, final T value)
{
redisTemplate.opsForHash().put(key, hKey, value);
}
/**
* 获取Hash中的数据
*
* @param key Redis键
* @param hKey Hash键
* @return Hash中的对象
*/
public <T> T getCacheMapValue(final String key, final String hKey)
{
HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
return opsForHash.get(key, hKey);
}
public void incrementCacheMapValue(String key,String hkey,int v){
redisTemplate.opsForHash().increment(key,hkey,v);
}
/**
* 删除Hash中的数据
*
* @param key
* @param hkey
*/
public void delCacheMapValue(final String key, final String hkey)
{
HashOperations hashOperations = redisTemplate.opsForHash();
hashOperations.delete(key, hkey);
}
/**
* 获取多个Hash中的数据
*
* @param key Redis键
* @param hKeys Hash键集合
* @return Hash对象集合
*/
public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys)
{
return redisTemplate.opsForHash().multiGet(key, hKeys);
}
/**
* 获得缓存的基本对象列表
*
* @param pattern 字符串前缀
* @return 对象列表
*/
public Collection<String> keys(final String pattern)
{
return redisTemplate.keys(pattern);
}
}
Redis序列化配置类
/**
* Redis使用FastJson序列化
*
* @author sg
*/
public class FastJsonRedisSerializer<T> implements RedisSerializer<T>
{
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private Class<T> clazz;
static
{
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
}
public FastJsonRedisSerializer(Class<T> clazz)
{
super();
this.clazz = clazz;
}
@Override
public byte[] serialize(T t) throws SerializationException
{
if (t == null)
{
return new byte[0];
}
return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
}
@Override
public T deserialize(byte[] bytes) throws SerializationException
{
if (bytes == null || bytes.length <= 0)
{
return null;
}
String str = new String(bytes, DEFAULT_CHARSET);
return JSON.parseObject(str, clazz);
}
protected JavaType getJavaType(Class<?> clazz)
{
return TypeFactory.defaultInstance().constructType(clazz);
}
}
8.封装权限认证
新增MYExpressionRoot类
@Component("ex")
public class MYExpressionRoot {
public boolean hasAuthority(String authority){
//获取当前用户的权限
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
List<String> permissions = loginUser.getPermissions();
//判断用户权限集合中是否存在authority
return permissions.contains(authority);
}
}
Controller层代码更改为
@RestController
public class HelloController {
@RequestMapping("/hello")
//@PreAuthorize("hasAuthority('system:detp:list11')")
@PreAuthorize("@ex.hasAuthority('system:detp:list')")
public String Hello(){
return "hello";
}
@RequestMapping("/testCors")
public ResponseResult testCors(){
return new ResponseResult(200,"testCors");
}
}
代码2:
@Service("ps")
public class PermissionService {
/**
* 判断当前用户是否具有permission
* @param permission 要判断的权限
* @return
*/
public boolean hasPermission(String permission){
//如果是超级管理员 直接返回true
if(SecurityUtils.isAdmin()){
return true;
}
//否则 获取当前登录用户所具有的权限列表 如何判断是否存在permission
List<String> permissions = SecurityUtils.getLoginUser().getPermissions();
return permissions.contains(permission);
}
}
LoginUser不用修改
9.SecurityUtils工具类
public class SecurityUtils
{
/**
* 获取用户
**/
public static LoginUser getLoginUser()
{
return (LoginUser) getAuthentication().getPrincipal();
}
/**
* 获取Authentication
*/
public static Authentication getAuthentication() {
return SecurityContextHolder.getContext().getAuthentication();
}
public static Boolean isAdmin(){
Long id = getLoginUser().getUser().getId();
return id != null && 1L == id;
}
public static Long getUserId() {
return getLoginUser().getUser().getId();
}
}
标签:
java
, spring boot
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通