首页 > 编程语言 > 详细

Spring Security【一】 ------ 前后端分离开发

时间:2021-06-24 23:00:10      阅读:27      评论:0      收藏:0      [点我收藏+]

之前项目中都是使用shiro作为安全框架,但是很多资料推荐spring security作为spring项目的安全框架。既然用着spring,那spring security自然还是要了解下的。

但是在实际接触中发现,比shori的学习成本高点。还有就是大部分都停留在将前端代码放在后端路径下,页面的跳转,重定向都由后端代码实现。这在当下的前后端分离开发中还是不合适的,所以经过我这几天的各种捣鼓,终于实现了speing security的前后端分离开发,后端主要处理接口数据,页面全部由前端处理,传输使用JSON格式。

一、代码结构

技术分享图片

 

 

二、实现过程

spring security底层实现就是一串过滤器,因此我们需要重写里面的一些方法,返回JSON格式。前端根据JSON数据进而实现用户的登录与身份验证等操作。具体代码如下

一、创建实体,实现 UserDetails接口,记得实现方法将boolean返回值改为true

@Setter
@Getter
public class BlogUser extends BaseEntity implements UserDetails {
 
    public BlogUser() {
    }
 
    public BlogUser(String userName, String userPassword) {
        this.userName = userName;
        this.userPassword = userPassword;
    }
 
    private static final Short ENABLE_FALSE = 0;
 
    /**
     * 用户名
     */
    private String userName;
 
    /**
     * 密码
     */
    private String userPassword;
 
    /**
     * 用户的角色
     * @return 角色组
     */
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }
 
    @Override
    public String getPassword() {
        return this.userPassword;
    }
 
    @Override
    public String getUsername() {
        return this.userName;
    }
 
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }
 
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }
 
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }
 
    @Override
    public boolean isEnabled() {
        return !this.enable.equals(ENABLE_FALSE);
    }

 

二、编写DAO层代码,实现UserDetailsService,重写loadUserByUsername方法

@Component
public class SelfUserDetailsServiceImpl implements UserDetailsService {

@Autowired
private BlogUserMapper mapper;

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
BlogUser blogUser = mapper.loadUserByUsername(username);
if(ObjectUtils.isEmpty(blogUser)){
throw new UsernameNotFoundException("根据用户名未找到用户信息");
}
return blogUser;
}
}


到此为止,security框架需要你做的事情已经完了。它会有一个默认登录页面(很丑,一看就是后端写的)。封装的还是很强大的。同时还支持前后端分离配置,不得不佩服spring的强大,下面看具体实现。

三、首先先看下security的配置类

@EnableWebSecurity
@Configuration
public class WebSecurityConfigure extends WebSecurityConfigurerAdapter {

/**
* 自定义登录认证
*/
@Autowired
private SelfAuthenticationProvider authenticationProvider;

/**
* 自定义登录成功处理器
*/
@Autowired
private UrlAuthenticationSuccessHandler authenticationSuccessHandler;

/**
* 自定义登录失败处理器
*/
@Autowired
private UrlAuthenticationFailureHandler authenticationFailureHandler;

/**
* 自定义注销处理器
*/
@Autowired
private UrlLogoutSuccessHandler logoutSuccessHandler;


/**
* 登录认证
* @param auth 登陆管理器
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) {
//添加自定义登陆认证
auth.authenticationProvider(authenticationProvider);
}

/**
* 具体配置登陆细节
* @param http 登陆访问对象
* @throws Exception 登陆异常
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
//关闭csrf
http.csrf().disable()
//关闭Session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
//开放api路径
.authorizeRequests().antMatchers("/api/**","/five-service/blog-article/search/**","/five-service/blog-article/point","/five-service/blog-user/login").
permitAll()
.anyRequest().authenticated()
//开启自动配置的登陆功能
.and()
//自定义登录请求路径(post请求)
.formLogin().usernameParameter("userName").passwordParameter("userPassword")
.loginProcessingUrl("/five-service/login")
//验证成功处理器
.successHandler(authenticationSuccessHandler)
//验证失败处理器
.failureHandler(authenticationFailureHandler).permitAll()
.and()
//关闭拦截未登录自动跳转,改为返回json信息
.exceptionHandling().authenticationEntryPoint(selfLoginUrlAuthenticationEntryPoint())
//开启自动配置的注销功能
.and()
.logout()
.logoutUrl("/five-service/logout")
//注销成功处理器
.logoutSuccessHandler(logoutSuccessHandler).permitAll()
.and()
//添加token过滤器
.addFilter(new TokenAuthenticationFilter(authenticationManagerBean()));
}

/**
* 身份认证失败处理类
* @return AuthenticationEntryPoint
*/
@Bean
public AuthenticationEntryPoint selfLoginUrlAuthenticationEntryPoint() {
return new SelfLoginUrlAuthenticationEntryPoint("/");
}

/**
* 重写方法,是上下文可以获取本地缓存对象
* @return AuthenticationManager 本地缓存对象
* @throws Exception 异常
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}

}

 

首先是自定义登录处理 SelfAuthenticationProvider

@Component
public class SelfAuthenticationProvider implements AuthenticationProvider {

//DAO查询用户
@Autowired
private SelfUserDetailsServiceImpl userDetailsService;

//密码加密解密
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
//表单输入的用户名
String username = (String) authentication.getPrincipal();
//表单输入的密码
String password = (String) authentication.getCredentials();
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
//对加密密码进行验证
if(bCryptPasswordEncoder.matches(password,userDetails.getPassword())){
return new UsernamePasswordAuthenticationToken(username,password,null);
}else {
throw new BadCredentialsException("密码错误");
}
}

@Override
public boolean supports(Class<?> aClass) {
return true;
}

 

然后是登陆成功处理类 UrlAuthenticationSuccessHandler

@Component
public class UrlAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException {
//security在分布式环境token使用,下一章会写道
Cookie token = TokenUtils.createToken(httpServletRequest);
httpServletResponse.addCookie(token);
httpServletResponse.setHeader("Content-Type", "application/json;charset=utf-8");
httpServletResponse.setStatus(200);
PrintWriter writer = httpServletResponse.getWriter();
writer.write(HttpResult.getJsonResult(200,"登陆成功"));
writer.flush();
writer.close();
}
}

 

然后是登录失败处理类 UrlAuthenticationFailureHandler 

@Component
public class UrlAuthenticationFailureHandler implements AuthenticationFailureHandler {

@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException {
httpServletResponse.setCharacterEncoding("UTF-8");
httpServletResponse.setStatus(401);
PrintWriter writer = httpServletResponse.getWriter();
writer.write(HttpResult.getJsonResult(401,"登陆失败"));
writer.flush();
writer.close();
}
}

 

最后是注销成功处理类

@Component
public class UrlLogoutSuccessHandler implements LogoutSuccessHandler {

@Override
public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException {
httpServletResponse.setCharacterEncoding("UTF-8");
httpServletResponse.setStatus(200);
PrintWriter writer = httpServletResponse.getWriter();
writer.write(HttpResult.getJsonResult(100,"注销成功"));
writer.flush();
writer.close();
}
}


至此后端代码就全部完成,前端根据JSON解析来完成登录及身份验证的全部动作。在配置类中可以看到我开启禁用session配置,目的是将用户信息存入redis,实现分布式身份验证需求。在单机环境下,可以开启session(默认开启),同时在过滤链中将自定义的TokenAuthenticationFilter去掉即可。

 

 

 

 

 

 


原文链接:https://blog.csdn.net/qq314499182/article/details/87913202

Spring Security【一】 ------ 前后端分离开发

原文:https://www.cnblogs.com/panchanggui/p/14928578.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!