基于 springboot+springsecurity 的前后端分离 实现
前几天改造旧系统springsecurity 传统mvc 改成 springboot 和前后端 分离。springsecurity 的概念我就不讲了,具体可以百度 看看 流程 ,其实就是一系列的过滤器链,职责分离,委托处理。
步骤 梭哈了 ,再也不用担心 自己不会springsecurity 前后端分离了
- 定义filter 加入 过滤链前面 ,禁用session,定义各种处理器
- 认证(自定义 UserDetails,UserDetailsService 的实现)成功后,生成token
- 自定义filter ,查看token redis 里面查找 认证信息,重新 写到上下文 认证信息 大功告成了基本
- 认证 信息 基本就是用户的菜单,权限,用户名 等 基本信息。
public class UserDetailsImpl implements UserDetails {
private String userId;
private String userName;
private String password;
private List<GrantedAuthority> authorities;
private List<MenuVO> menuList;
}
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserDao userDao;
private Logger logger = Logger.getLogger(UserDetailsServiceImpl.class);
public org.springframework.security.core.userdetails.UserDetails loadUserByUsername(String userName)
throws UsernameNotFoundException {
User user = this.userDao.findByUserName(userName);
}
}
/**
* token 过滤器
*/
@Component
public class TokenAuthenticationTokenFilter extends OncePerRequestFilter {
@Autowired
private AuthenticationFailureHandler authenctiationFailureHandler;
@Autowired
private RedisUtils redisUtils;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// 登陆请求 校验验证码
if (StringUtils.equals("/account/doLogin", request.getRequestURI())
&& StringUtils.equalsIgnoreCase(request.getMethod(), "POST")) {
try {
//validate(request);
} catch (ValidateCodeException e) {
// 有异常就返回自定义失败处理器
authenctiationFailureHandler.onAuthenticationFailure(request, response, e);
return;
}
}
String token = request.getHeader(UserConstant.TOKEN);
if (token != null ) {
String realToken = MessageFormat.format(RedisKey.SYSTEM_TOKEN, token);
UserGrantedAuthority userGrantedAuthority = redisUtils.stringGet(realToken,UserGrantedAuthority.class);
if (userGrantedAuthority != null && SecurityContextHolder.getContext().getAuthentication() == null) {
List<GrantedAuthority> grantedAuthorities = userGrantedAuthority.getAuthorities().stream().map(e -> new SimpleGrantedAuthority(e)).collect(Collectors.toList());
String userId = userGrantedAuthority.getUserId();
String username = userGrantedAuthority.getUsername();
String password = userGrantedAuthority.getPassword();
List<MenuVO> menus = userGrantedAuthority.getMenus();
UserDetails userDetails = new UserDetailsImpl(userId,username,password,grantedAuthorities,menus);
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails, null, grantedAuthorities);
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
// 刷新token
redisUtils.exportCache(realToken,UserConstant.TOKEN_EXPIRE, TimeUnit.MINUTES);
// 刷新online list时间
String userIdListKey = MessageFormat.format(RedisKey.SYSTEM_TOKEN_ONLINE, SecureUtil.md5(String.valueOf(userId)));
redisUtils.exportCache(userIdListKey,UserConstant.TOKEN_EXPIRE, TimeUnit.MINUTES);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
filterChain.doFilter(request, response);
}
/**
* 校验验证码
* @param request
*/
private void validate(HttpServletRequest request) {
String uuid = request.getParameter("uuid");
String code = redisUtils.stringGet(MessageFormat.format(RedisKey.PICTURE_VERIFICATION_CODE, uuid));
String verify = request.getParameter("verify");
if (StringUtils.isBlank(code)) {
throw new ValidateCodeException("验证码已过期!");
}
if (!StringUtils.equalsIgnoreCase(code, verify)) {
throw new ValidateCodeException("验证码不匹配");
}
redisUtils.remove(code);
}
}
// 核心配置
@Override
protected void configure(HttpSecurity http) throws Exception {
// 禁用 csrf, 由于使用的是JWT,我们这里不需要csrf
http.cors().and().csrf().disable()
// 基于token,所以不需要session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests()
// 基于token,所以不需要session
// 跨域预检请求
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
// 登录URL
.antMatchers("/account/doLogin").permitAll()
.antMatchers("/imageGen/getSysManageLoginCode").permitAll()
//.antMatchers("/**").permitAll()
// swagger
.antMatchers("/swagger**/**").permitAll()
.antMatchers("/doc.html").permitAll()
.antMatchers("/webjars/**").permitAll()
.antMatchers("/v2/**").permitAll()
.antMatchers("/profile/**").permitAll()
.antMatchers("/swagger-ui.html").permitAll()
.antMatchers("/swagger-resources/**").permitAll()
.antMatchers("/webjars/**").permitAll()
.antMatchers("/*/api-docs").permitAll()
.antMatchers("/druid/**").permitAll()
// 其他所有请求需要身份认证
.anyRequest().authenticated()
.accessDecisionManager(roleAccessDecisionManager)
.and()
.headers().frameOptions().disable()
.and()
.formLogin() //开启登录
.loginProcessingUrl("/account/doLogin")
.passwordParameter("loginPassWord").usernameParameter("userName")
.successHandler(authenticationSuccessHandler) // 登录成功
.failureHandler(authenticationFailureHandler) // 登录失败
.permitAll();
// 退出登录处理器
//http.logout().logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler());
// 开启登录认证流程过滤器
http.addFilterBefore(tokenAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
http.logout().logoutUrl("/account/logout").logoutSuccessHandler(logoutSuccessHandler);
http.exceptionHandling().accessDeniedHandler(new SimpleAccessDeniedHandler()).authenticationEntryPoint(new UnauthorizedEntryPoint());
}
核心代码 如下
是不是 超级简单啊,答案 是的
elk