SpringSecurity开发文档
AI-摘要
切换
KunKun GPT
AI初始化中...
介绍自己
生成本文简介
推荐相关文章
前往主页
前往tianli博客
一、引入依赖
<!-- 有默认的登录页面/login,有默认的账号密码,默认的认证,默认退出界面/logout -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
直接引入后,需要解决的问题:
- SpringSecurity默认提供了一个账号密码(账号是user,密码会随机生成一个),在登录时,(框架)会自动比较。但是我们需要自己从数据库中查询出账号密码,再(让框架自动)比较。
- SpringSecurity默认提供了一个/login请求下的登录页面。现在是前后端分离程序,需要我们自定义/login请求和登录页面。
- SpringSecurity框架如何融入JWT,token。
- SpringSecurity框架如何进行授权
二、编写配置类
注入密码加密器、自定义认证管理器、重写登录请求和登录过滤器。
//启用授权检查
@EnableGlobalMethodSecurity(prePostEnabled=true)
@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
JwtAuthentiactionFilter jwtAuthentiactionFilter;
//注入密码加密器
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
//注入认证管理器,自定义实现认证过程
@Bean
@Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
//重写SpringSecurity的默认配置文件(里面包含了直接跳转到默认登录页面的配置),放行自定义login请求,拦截其他请求
@Override
protected void configure(HttpSecurity http) throws Exception {
http
//csrf跨站攻击
.csrf().disable()
//禁用Session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
//JWTFilter在Security内置过滤器之前执行
.addFilterBefore(jwtAuthentiactionFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
//特殊路径放行,指定特殊路径可以匿名访问
.antMatchers("/user/login").anonymous()
.anyRequest().authenticated();
}
}
三、创建登录实体类
@NoArgsConstructor
@AllArgsConstructor
@Data
public class User {
private Integer uid;
private String uname;
private String password;
}
四、自定义数据库查询方法
继承UserDetailsService接口,自定义数据库查询。
@Service
public class LoginUserDetailsServiceImpl implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user=null;
List<String> permissions=new ArrayList<>();
//从数据库根据用户名查询用户信息
if("a".equals(username)){
user=new User(1,"a","$2a$10$CEhZfCMH2KzVVT0Ex6UoDeDLyZ51WevCXKshr3X63kOzrjCYW70d2");
//从数据库根据用户信息查询用户的权限
permissions.add("test");
permissions.add("add");
}else if("b".equals(username)){
user=new User(2,"b","$2a$10$SXKszBA016Vpc97fbWh5L.1MrEHF0k5jd5qyrbWkc1feuDSeGrNk6");
permissions.add("test");
}else{
throw new RuntimeException("用户名不存在");
}
//UserDetails存入redis中
UserDetails userDetails=new LoginUserDetails(user,permissions);
return userDetails;
}
}
五、封装数据库查询的数据
@NoArgsConstructor
@AllArgsConstructor
@Data
public class LoginUserDetails implements UserDetails {
//从数据库查询的用户信息
private User user;
private List<String> permissions;
//后面授权要使用的
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> authorities=new ArrayList<>();
permissions.stream()
.forEach(s -> {
SimpleGrantedAuthority authority = new SimpleGrantedAuthority(s);
authorities.add(authority);
});
return authorities;
}
//与前端传递过来的密码自动比较,一定要获取查询密码
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUsername();
}
//这些判断以后再Filter通过token实现,都让他们返回true
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
六、controller层定义login登录请求
@RestController
public class UserController {
@Autowired
UserService userService;
@RequestMapping("/user/login")
public ResponseResult login(@RequestBody User user, HttpServletResponse response){
//验证账号密码,底层会校验
//底层校验通过,发token,写入响应头,响应前端
String token = userService.login(user);
response.setHeader("token", token);
response.setHeader("Access-Control-Expose-Headers", "token");
return new ResponseResult(200,"ok","login suceess");
}
}
七、service层自定义认证管理器
@Service
public class UserServiceImpl implements UserService {
@Autowired
AuthenticationManager authenticationManager;
@Override
public String login(User user) {
//用户名密码,校验通过,返回token
//密码比较,不需要再去实现,底层认证器已经实现了
//Object principal, 身份信息:用户名,用户对象
// Object credentials, 凭证:密码
//获取前端的用户名密码,封装成UsernamePasswordAuthenticationToken对象
UsernamePasswordAuthenticationToken authenticationToken =
new UsernamePasswordAuthenticationToken(user.getUname(), user.getPassword());
//调用认证管理器的认证方法,认证失败底层自动抛异常
Authentication authenticate = authenticationManager.authenticate(authenticationToken);
//认证成功,返回值获取身份信息,就是LoginUserDetails对象
LoginUserDetails userDetails = (LoginUserDetails) authenticate.getPrincipal();
System.out.println("验证器验证通过,返回Principal身份信息:"+userDetails);
//认证成功,生成token
return JwtUtil.createRereshToken(userDetails.getUser().getUid()+"",user.getUname());
}
}
不需要创建mapper,SpringSecurity会根据写好的查询逻辑,自动查询并和传过来的前端对象自动进行比较。
八、创建JWT的token登录拦截器
//OncePerRequestFilter是Spring框架封装的过滤器
@Component
public class JwtAuthentiactionFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
//请求携带token,进行token校验
String token = request.getHeader("token");
if(StringUtils.isEmpty(token)){
//没有token,放行其实是拦截:SpringSecurity框架底层有拦截器
filterChain.doFilter(request, response);
return;
}
if(!JwtUtil.validate(token)){
throw new RuntimeException("token无效");
}
//token有效,记录到Security上下文中,告诉后面的拦截器,可以放行
//可以从redis获取完整的用户信息
//现在没有存,还是模拟数据库查询
String username = JwtUtil.getEname(token);
User user=null;
List<String> permissions=new ArrayList<>();
//从数据库根据用户名查询用户信息
if("a".equals(username)){
user=new User(1,"a","$2a$10$CEhZfCMH2KzVVT0Ex6UoDeDLyZ51WevCXKshr3X63kOzrjCYW70d2");
permissions.add("test");
permissions.add("add");
}else if("b".equals(username)){
user=new User(2,"b","$2a$10$SXKszBA016Vpc97fbWh5L.1MrEHF0k5jd5qyrbWkc1feuDSeGrNk6");
permissions.add("test");
}else{
throw new RuntimeException("用户名不存在");
}
LoginUserDetails userDetails=new LoginUserDetails(user,permissions);
//一定要用三个的参数:第三个参数是权限,super.setAuthenticated(true);
//Object principal, 身份信息
// Object credentials, 凭证,我们使用token,null不影响使用
//Collection<? extends GrantedAuthority> authorities,要填入查询到的权限
Authentication authentication=new UsernamePasswordAuthenticationToken(userDetails,null,userDetails.getAuthorities());
//将通过token验证的信息,写入sercuity上下文,权限框架的过滤器就不会再拦截了
SecurityContextHolder.getContext().setAuthentication(authentication);
System.out.println("JWT通过");
filterChain.doFilter(request, response);
}
}
- 感谢你赐予我前进的力量
赞赏者名单
因为你们的支持让我意识到写文章的价值🙏
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 Gavana
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果