SpringBoot整合SpringSecurity

SpringBoot整合SpringSecurity

依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
     <groupId>com.alibaba</groupId>
     <artifactId>fastjson</artifactId>
     <version>1.2.75</version>
</dependency>
    <dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>    

tips:整合SpringSecurity时,使用到Redis和JWT.需要到的RedisUtil和JwtUitl自己封装好。

SpringSecurity自带用户认证

当SpringBoot只引入SpringSecurity,还没有配置任何配置和自定的时候,SpringSecurity会自动生成一个用户进行认证,用户名默认为user,密码在控制台打印。

当访问接口的时候,会进行认证。SpringSecurity使用自动生成一个用户进行认证

一、让SpringSecurity使数据库中的用户名进行认证

需要继承的接口:

public interface UserDetailsService {
    //通过重写这个方法,通过用户名到数据库查询出用户,将用户一些信息封装成UserDetails并返回
	UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}

//将用户信息封装成UserDetails的实现类
public interface UserDetails extends Serializable {
    //用户权限
	Collection<? extends GrantedAuthority> getAuthorities();
    //获取密码
	String getPassword();
    //用户名
	String getUsername();
    //根据用户情况设置用户状态
	boolean isAccountNonExpired();
	boolean isAccountNonLocked();
	boolean isCredentialsNonExpired();
	boolean isEnabled();
}

实现类

@Service
public class UserDetailsImpl implements UserDetailsService {

    @Autowired
    userMessageMapper userMessageMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //查询用户信息
        User user = (User) userMessageMapper.selectOne(new QueryWrapper<User>().eq("username",username));
        //如果没有查询到用户,就抛出异常
        if(Objects.isNull(user)){
            //认证链会捕获
            throw new RuntimeException("用户名错误");
        }
        //查询对应的权限信息(权限信息应从数据库中查询出来,这里为了方便,就先写死了)
        ArrayList<String> permisson = new ArrayList<>(Arrays.asList("test","admin"));
        //封装成UserDetails
        LoginUser loginUser = new LoginUser(user,permisson);
        return loginUser;
    }
}

@Data
@NoArgsConstructor
@AllArgsConstructor
public class LoginUser implements UserDetails {

    private User user;
    private List<String> permission;
    public LoginUser(User user,List<String> permission){
        this.user=user;
        this.permission=permission;
    }
    @JSONField(serialize = false)//SimpleGrantedAuthority无法序列化
    private List<SimpleGrantedAuthority> authorities;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        if(authorities!=null){
            return authorities;
        }
        
        //把permission中String类型的权限信息封装成SimplGrantedAuthority对象
        authorities= new ArrayList<>();

        permission.forEach((p)->{
            authorities.add(new SimpleGrantedAuthority(p));
        });
        return authorities;
    }

    @Override
    public String getPassword() {
        return user.getPassword();
    }

    @Override
    public String getUsername() {
        return user.getName();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

SpringSecurity配置

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
   

     //此时数据库中的密码不能为明文密码,应为BCryptPassword编码过的密码
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                //关闭csrf
                .csrf().disable()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .anyRequest()
                .authenticated()//所有请求都需要访问
        ;
        //允许跨域
        http.cors();
    }
}

tips:到这里,前面SpringSecurity自动生成用户的部分,我们已经替换成自己的了,用户密码认证的时候SpringSecurity已经会去数据库查询并验证。验证通过则让继续访问接口,验证不通过,则不让访问接口。

进行登录认证和授权

@Service
public class LoginServiceImpl implements LoginService {

    //AuthenticationManager anthenticate进行用户认证.需要在SpringSecurit容器ronrong
    /**
    *  在SpringSecurity中添加下面代码
    *  @Override
    *  @Bean
    *  public AuthenticationManager authenticationManagerBean() throws Exception {
    *       return super.authenticationManagerBean();
    *  }
    */
    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    JwtUtil jwtUtil;

    @Override
    public Result login(User user) {

        //如果认证没通过,给出对应的提示
        //如果认证通过了,使用userid生成一个jwt存入ResponseResult返回
        //把完整的用户信息存入redis userid作为key
        UsernamePasswordAuthenticationToken usernamePassword= new UsernamePasswordAuthenticationToken(user.getName(), user.getPassword());
        Authentication authenticate = authenticationManager.authenticate(usernamePassword);
        if(Objects.isNull(authenticate)){
            throw new RuntimeException("登录失败");
        }
        //认证通过,使用userId生成一个jwt,jwt存入Result返回
        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
        String userId = String.valueOf(loginUser.getUser().getId());
        String name = loginUser.getUser().getName();
        String jwt = jwtUtil.generateJWT(userId, name);
        Map<String,String> map =new HashMap<>();
        map.put("token",jwt);
        //把完整的用户信息存入redis,userId作为key
        redisUtil.stringSet("login:"+userId,loginUser);
        return Result.success(200,"登录成功",map);
    }

    @Override
    public Result logout() {
        //获取SecurityContextHolder中的用户id
        UsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        int id = loginUser.getUser().getId();
        redisUtil.stringDelete("login:"+id);
        //删除redis中的值
        return Result.success("退出成功");
    }
}
@RestController
public class LoginController {

    @Autowired
    LoginService loginService;

    @PostMapping("/user/login")
    public Result login(@RequestBody User user){
        //登录
        return loginService.login(user);
    }

    @PostMapping("/user/logout")
    public Result logout(){
        return loginService.logout();
    }

}
整合JWT进行认证

编写JWT过滤器

@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    @Autowired
    JwtUtil jwtUtil;

    @Autowired
    RedisUtil redisUtil;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //获取token
        String token = request.getHeader("token");
        if(!StringUtils.hasText(token)){
            //没有token,放行,后面的过滤器会验证
            filterChain.doFilter(request,response);
            //不能往下继续执行
            return;
        }
        String userId;
        try{
            Claims claims = jwtUtil.parseJWT(token);
            userId = claims.getId();
        }catch (Exception e){
            e.printStackTrace();
            throw new RuntimeException("token非法");
        }
        //解析token
        //从redis获取用户信息
        //存入SecurityContextHolder
        String redisKey="login:"+userId;
        LoginUser loginUser  = (LoginUser) redisUtil.stringGet(redisKey);
        if(Objects.isNull(loginUser)){
            throw new RuntimeException("token非法");
        }
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        filterChain.doFilter(request,response);
    }
}
自定义认证失败和授权失败处理器

//认证失败处理器
@Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {


    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        Result fail = Result.fail(HttpStatus.UNAUTHORIZED.value(), "请求失败", "用户认证失败");
        //处理异常
        response.setStatus(200);
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        String message= JSON.toJSONString(fail);
        response.getWriter().print(message);
    }
}

//授权失败处理类
@Component
public class AccessDeniedHandlerImpl implements AccessDeniedHandler {

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        response.setStatus(200);
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        Result fail = Result.fail(HttpStatus.FORBIDDEN.value(), "请求失败", "权限不足");
        String message = JSON.toJSONString(fail);
        response.getWriter().print(message);
    }
}
跨域配置

@Configuration
public class CorsConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {

        registry.addMapping("/**")//跨域的路径
                .allowedOriginPatterns("*")
                .allowedHeaders("*")
                .allowedMethods("GET","POST","DELETE","PUT")
                .allowCredentials(true)
                .maxAge(3600)
        ;
    }

}
SpringSecurity完整配置

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

    @Autowired
    private AuthenticationEntryPointImpl authenticationEntryPoint;

    @Autowired
    private AccessDeniedHandlerImpl accessDeniedHandler;

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                //关闭csrf
                .csrf().disable()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers("/hello")
                .permitAll()
                 //对于登录接口 允许匿名访问
                .antMatchers("/user/login")
                .anonymous()//登录之后不能再访问登录接口
                .anyRequest()
                .authenticated()
        ;
        http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
        http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint).accessDeniedHandler(accessDeniedHandler);
        //允许跨域
        http.cors();
    }
}

tips:SpringScurity整合完整。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

魔宇-梦宇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值