java前后端分离权限_Spring Security实现前后端分离登录与权限控制

通过Security实现前后端分离的登录与权限控制

一、引入Security配置文件

org.springframework.boot

spring-boot-starter-security

二、新建SecurityConfiguration配置类

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.annotation.Configuration;

import org.springframework.http.HttpMethod;

import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;

import org.springframework.security.config.annotation.web.builders.HttpSecurity;

import org.springframework.security.config.annotation.web.builders.WebSecurity;

import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;

import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

import org.springframework.security.core.userdetails.UserDetailsService;

import org.springframework.security.crypto.password.PasswordEncoder;

import org.springframework.security.web.AuthenticationEntryPoint;

import org.springframework.security.web.access.AccessDeniedHandler;

import org.springframework.security.web.authentication.AuthenticationFailureHandler;

import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

import javax.annotation.Resource;

@Configuration

@EnableWebSecurity

public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

@Resource(name = "UserDetailsServiceImpl")

private UserDetailsService userDetailsService;

@Resource

private PasswordEncoder passwordEncoder;

@Autowired

private AuthenticationSuccessHandler loginSuccessHandler;

@Autowired

private AuthenticationFailureHandler loginFailureHandler;

@Autowired

private AuthenticationEntryPoint authenticationEntryPoint;

@Autowired

private AccessDeniedHandler accessDeniedHandler;

@Autowired

private LogoutSuccessHandler logoutSuccessHandler;

@Override

protected void configure(HttpSecurity httpSecurity) throws Exception {

httpSecurity

.authorizeRequests()

//认证规则

.anyRequest().access("@rbacService.hasPermission(request,authentication)")

.and()

//开启表单登录

.formLogin().loginPage("/login").permitAll()

//登录失败处理

.failureHandler(loginFailureHandler)

//登录成功处理

.successHandler(loginSuccessHandler)

.and()

//没有权限处理

.exceptionHandling().accessDeniedHandler(accessDeniedHandler)

.and()

//未登录处理

.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint)

.and()

//退出成功处理

.logout().logoutSuccessHandler(logoutSuccessHandler).permitAll();

//开启跨域访问

httpSecurity.cors().disable();

//开启模拟请求,比如API POST测试工具的测试,不开启时,API POST为报403错误

httpSecurity.csrf().disable();

}

@Override

public void configure(WebSecurity web) {

//对于在header里面增加token等类似情况,放行所有OPTIONS请求。

web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");

// web.ignoring().antMatchers("/index.html", "/static/**", "/login_p", "/favicon.ico")

// // 给 swagger 放行;不需要权限能访问的资源

// .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/images/**", "/webjars/**", "/v2/api-docs", "/configuration/ui", "/configuration/security");

}

@Override

protected void configure(AuthenticationManagerBuilder auth) throws Exception {

auth.userDetailsService(userDetailsService)

.passwordEncoder(passwordEncoder);

}

三、实现未登录、登录成功、登录失败、没有权限、退出登录成功时的接口

1、未登录

@Component

public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {

@Autowired

private ObjectMapper objectMapper;

@Override

public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {

httpServletResponse.setContentType("application/json;charset=utf-8");

PrintWriter out = httpServletResponse.getWriter();

//ResponserJSON为自定义的用于返回JSON的类

out.write(objectMapper.writeValueAsString(new ResponseJSON().setUnLoginError()));

out.flush();

out.close();

}

}

2、登录成功

@Component

public class LoginSuccessHandlerImpl implements AuthenticationSuccessHandler {

@Autowired

private ObjectMapper objectMapper;

@Override

public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {

httpServletResponse.setContentType("application/json;charset=utf-8");

PrintWriter out = httpServletResponse.getWriter();

//ResponserJSON为自定义的用于返回JSON的类

out.write(objectMapper.writeValueAsString(new ResponseJSON().setSuccess("登录成功")));

out.flush();

out.close();

}

}

3、登录失败

@Component

public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler {

@Autowired

private ObjectMapper objectMapper;

@Override

public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {

httpServletResponse.setContentType("application/json;charset=utf-8");

PrintWriter out = httpServletResponse.getWriter();

//ResponserJSON为自定义的用于返回JSON的类

out.write(objectMapper.writeValueAsString(new ResponseJSON().setSuccess("注销成功")));

out.flush();

out.close();

}

}

4、没有权限

@Component

public class AccessDeniedHandlerImpl implements AccessDeniedHandler {

@Autowired

private ObjectMapper objectMapper;

@Override

public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {

httpServletResponse.setContentType("application/json;charset=utf-8");

PrintWriter out = httpServletResponse.getWriter();

//ResponserJSON为自定义的用于返回JSON的类

out.write(objectMapper.writeValueAsString(new ResponseJSON().setError("没有权限")));

out.flush();

out.close();

}

}

5、退出登录成功

@Component

public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler {

@Autowired

private ObjectMapper objectMapper;

@Override

public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {

httpServletResponse.setContentType("application/json;charset=utf-8");

PrintWriter out = httpServletResponse.getWriter();

//ResponserJSON为自定义的用于返回JSON的类

out.write(objectMapper.writeValueAsString(new ResponseJSON().setSuccess("注销成功")));

out.flush();

out.close();

}

}

四、实现UserDetail接口

import lombok.Data;

import org.springframework.security.core.GrantedAuthority;

import org.springframework.security.core.userdetails.UserDetails;

import org.springframework.stereotype.Component;

import java.util.Collection;

@Component

@Data

//@Data为lombok的注解

public class User implements UserDetails {

//用户名

private String username;

//密码

private String password;

//非必须,根据需求可以定义更多的字段

private String phone;

private Collection extends GrantedAuthority> authorities;

public void setUsername(String username) {

this.username = username;

}

public void setPassword(String password) {

this.password = password;

}

public void setAuthorities(Collection extends GrantedAuthority> authorities) {

this.authorities = authorities;

}

@Override

public Collection extends GrantedAuthority> getAuthorities() {

return this.authorities;

}

@Override

public String getPassword() {

return this.password;

}

@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 true;

}

}

五、实现UserDetailService接口

@Component("UserDetailsServiceImpl")

public class UserDetailsServiceImpl implements UserDetailsService {

//获取数据库中的user相关数据,自行设计

@Resource

private UserMapper userMapper;

@Override

public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

String password = userMapper.getPassword(username);

if (StringUtils.isBlank(password)){

throw new UsernameNotFoundException("账号不存在!");

}

User myUserDetail = new User();

myUserDetail.setUsername(username);

myUserDetail.setPassword(password);

return myUserDetail;

}

}

此类的作用是获取传入的username对应的密码,可以根据实际情况进行修改,账号不存在时可抛出UsernameNotFoundException异常

实现PasswordEncode接口

@Component

public class PasswordEncoderImpl implements PasswordEncoder {

@Override

public String encode(CharSequence charSequence) {

return charSequence.toString();

}

@Override

public boolean matches(CharSequence charSequence, String s) {

return encode(charSequence).equals(s);

}

}

此类主要用于对密码的加密以及与数据库中的密码进行比对,如果想要偷懒,也可以使用官方的NoOpPasswordEncoder(不推荐)类或BCryptPasswordEncoder类 ,使用方法为将SecurityConfiguration中的authenticationProvider方法的

authenticationProvider.setPasswordEncoder(passwordEncoder);

改为

authenticationProvider.setPasswordEncoder(NoOpPasswordEncoder.getInstance());

Spring5后强制要求密码不能明文传输,NoOpPasswordEncoder(不推荐)类为不加密的实现类,不安全,不推荐使用,可以使用Spring Security 中提供了BCryptPasswordEncoder密码编码工具

六、权限控制

通过编写自定义的service对权限进行控制

public interface RbacService {

boolean hasPermission(HttpServletRequest request, Authentication authentication);

}

@Component("rbacService")

public class RbacServiceImpl implements RbacService {

private AntPathMatcher antPathMatcher = new AntPathMatcher();

@Resource

private UserMapper userMapper;

@Override

public boolean hasPermission(HttpServletRequest request, Authentication authentication) {

Object principal = authentication.getPrincipal();

boolean hasPermission = false;

if (principal instanceof UserDetails) { //首先判断先当前用户是否是我们UserDetails对象。

String userName = ((UserDetails) principal).getUsername();

//连接数据库获取用户有权限的url,根据业务自行修改

List urls = userMapper.getMenuByUserName(userName);

// 注意这里不能用equal来判断,因为有些URL是有参数的,所以要用AntPathMatcher来比较

for (String url : urls) {

if (antPathMatcher.match(url, request.getRequestURI())) {

hasPermission = true;

break;

}

}

}

return hasPermission;

}

}

编写完后通过在SecurityConfiguation中添加如下代码实现权限控制

.anyRequest().access("@rbacService.hasPermission(request,authentication)")

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SpringBoot和Vue前后端分离的解决方案是非常常见的,而SpringSecurity则是SpringBoot中处理权限问题的最佳解决方案之一。下面介绍一下如何使用SpringSecurity完美处理权限问题。 1. 引入SpringSecurity依赖 在pom.xml中引入SpringSecurity依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> ``` 2. 配置SpringSecuritySpringBoot应用中,可以通过继承WebSecurityConfigurerAdapter类来配置SpringSecurity,代码如下: ```java @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private CustomUserService customUserService; @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/", "/index").permitAll() .antMatchers("/admin/**").hasRole("ADMIN") .anyRequest().authenticated() .and() .formLogin().loginPage("/login").defaultSuccessUrl("/home").permitAll() .and() .logout().logoutUrl("/logout").logoutSuccessUrl("/login").permitAll(); } @Override public void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(customUserService).passwordEncoder(new BCryptPasswordEncoder()); } @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/static/**"); } } ``` 3. 自定义用户认证 在上面的代码中,我们使用了自定义的用户认证服务CustomUserService。CustomUserService实现了UserDetailsService接口,代码如下: ```java @Service public class CustomUserService implements UserDetailsService { @Autowired private UserRepository userRepository; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = userRepository.findByUsername(username); if (user == null) { throw new UsernameNotFoundException("用户名不存在!"); } List<GrantedAuthority> authorities = new ArrayList<>(); for (Role role : user.getRoles()) { authorities.add(new SimpleGrantedAuthority(role.getName())); } return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), authorities); } } ``` 在上面的代码中,我们使用了自定义的UserRepository来获取用户信息,然后将用户的角色转换成GrantedAuthority对象,返回给SpringSecurity。 4. 在Vue中处理权限问题 在Vue中,我们可以通过Vue Router的导航守卫来处理权限问题。代码如下: ```javascript import router from './router' import store from './store' router.beforeEach((to, from, next) => { if (to.meta.requireAuth) { // 判断该路由是否需要登录权限 if (store.state.token) { // 获取当前的token是否存在 next() } else { next({ path: '/login', query: {redirect: to.fullPath} // 将跳转的路由path作为参数,登录成功后跳转到该路由 }) } } else { next() } }) ``` 在上面的代码中,我们通过store来获取当前用户的token,如果token存在则允许访问,否则跳转到登录页面。 以上就是使用SpringSecurity处理权限问题的完整解决方案。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值