1 ,引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2 ,配置 Security
extends WebSecurityConfigurerAdapter 重写 configure(HttpSecurity http)方法
配置http
http.csrf().disable();
//开启跨域
http.cors();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.authorizeRequests()
.antMatchers(URL_).permitAll()//放行 URL_中路径(URL_中的路径无需认证就可访问)
.anyRequest().authenticated();
3自定义登录user password 用户名 密码
实现 implements UserDetailsService 重写里面方法就一个
// 此类上记得注入到spring容器中 @Service
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//根据username在数据库里查询用户 返回 自定义类 ,其中自定义类实现 UserDetails
//sysUser 为数据库中的user的信息
//list 为用户所拥有的权限信息
return new LoginUser(sysUser,list);
}
package com.tianxuan.springscecurity.config.security;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.tianxuan.springscecurity.entity.SysUser;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
/**
* Created with IntelliJ IDEA.
* Description:
* User: WY
* Date: 2023-04-12
* Time: 13:03
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
@JsonIgnoreProperties({"enabled","accountNonExpired","username","password"
, "accountNonLocked", "credentialsNonExpired","authorities"})
public class LoginUser implements UserDetails , Serializable {
private static final long serialVersionUID = 1L;
private SysUser sysUser;
private List<String> rode;
/**
* 用户所拥有的权限
* @return
*/
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<SimpleGrantedAuthority> collect = rode.stream()
.distinct()
.map(SimpleGrantedAuthority::new).collect(Collectors.toList());
return collect;
}
@Override
public String getPassword() {
// 前面传来的密码
return sysUser.getPassword();
}
@Override
public String getUsername() {
//前面传来的账户
return sysUser.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;
}
}
配置类中记得注入
/**
* 密码加密
* @return
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
JWT
package com.tianxuan.springscecurity.config.security;
import cn.hutool.core.util.StrUtil;
import com.tianxuan.springscecurity.utils.JwtUtils;
import io.jsonwebtoken.Claims;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Created with IntelliJ IDEA.
* Description:
* User: WY
* Date: 2023-04-12
* Time: 13:48
*/
@Configuration
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Resource
private RedisTemplate redisTemplate;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String token = request.getHeader("token");
if (StrUtil.isBlank(token)){
filterChain.doFilter(request,response);
return;
}
// 2、对token进行解析,取出其中的userId
String userId = null ;
try {
Claims claims = JwtUtils.parseJWT(token);
userId = claims.getId();
}catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("token非法") ;
}
// 3、使用userId从redis中查询对应的LoginUser对象
LoginUser loginUser = (LoginUser) redisTemplate.opsForValue().get("login:" + userId);
// SysUser loginUser = JSON.parseObject(String.valueOf(loginUserJson.getSysUser()), SysUser.class);
if(loginUser != null) {
// 4、然后将查询到的LoginUser对象的相关信息封装到UsernamePasswordAuthenticationToken对象中,然后将该对象存储到Security的上下文对象中
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null , loginUser.getAuthorities()) ;
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
// 5、放行
filterChain.doFilter(request,response);
}
}
把jwt放到 UsernamePasswordAuthenticationFilter.class之前(JwtAuthenticationFilter 注入到配置类中)
http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
实现 implements AccessDeniedHandler 权限信息 全选不足会被拦截到这
@Component
public class AccessDeniedHandlerImpl implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) {
System.out.println("权限不足》》");
System.out.print(accessDeniedException.getMessage());
}
}
实现 implements AuthenticationEntryPoint(认证信息)同上
@Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
response.setContentType("application/json;charset=UTF-8");
ServletOutputStream outputStream = response.getOutputStream();
HashMap<String, Object> map = new HashMap<>();
map.put("getMessage",authException.getMessage());
map.put("getLocalizedMessage",authException.getLocalizedMessage());
outputStream.write(JSON.toJSONBytes(R.fail(map.toString())));
System.out.println();
outputStream.write(JSON.toJSONBytes("--------------------"));
System.out.println();
outputStream.write(JSON.toJSONBytes(R.fail("用户名密码错误")));
outputStream.flush();
outputStream.close();
System.out.println("认证失败请重新登录》》"+authException.getMessage());
}
}
配置类中设置
//把前面的两个认证全选注入进来
http
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler)
.authenticationEntryPoint(authenticationEntryPointImpl);
登录退出
// 配置类中注入
/**
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
*/
@Resource
private AuthenticationManager authenticationManager;
@PostMapping("/login")
public R login(SysUser sysUser) {
// 自动找到 UserDetailsService实现的类 判断用户名和密码
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken
= new UsernamePasswordAuthenticationToken(sysUser.getUsername(), sysUser.getPassword());
Authentication authenticate = authenticationManager.authenticate(usernamePasswordAuthenticationToken);
LoginUser principal = (LoginUser) authenticate.getPrincipal();
String token = JwtUtils.createJWT(principal.getSysUser().getId().toString(), "999", 60 * 60 * 1000 * 24);
principal.getSysUser().setToken(token);
String s = JSON.toJSONStringWithDateFormat(principal.getSysUser(), "yyyy-MM-dd", SerializerFeature.WriteDateUseDateFormat);
redisTemplate.opsForValue().set("login:" + principal.getSysUser().getId(), principal);
Map<String, Object> map = new HashMap<>();
map.put("token", token);
return R.ok(map);
}
@GetMapping("/logOut")
public R logOut() {
LoginUser loginUser = (LoginUser)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
Integer id = loginUser.getSysUser().getId();
redisTemplate.delete("login:"+id);
return R.ok("退出成功");
}