SpringBoot 2.x前后端分离项目中使用 SpringSecurity,自定义校验

5 篇文章 0 订阅
1 篇文章 0 订阅

demo git地址:传送门

一:前言

	现在绝大部分都是前后端项目,在硬编码中,跳转到某一页面路径,显然不合适,所以应该是服务端返回JSON,
来告知前端是否登录,是否拥有页面权限,登录是否超时等,前端根据返回JSON约定值,来自由跳转。

二:具体实现

1、建立登录成功,失败,无权限等状态配置类

AccessDeniedHandlerConfig (无权限)

@Component
public class AccessDeniedHandlerConfig implements AccessDeniedHandler {

    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
        JSONObject result = new JSONObject();
        result.put("msg","无权限!");
        httpServletResponse.setContentType("application/json;charset=utf-8");
        httpServletResponse.getWriter().println(result.toJSONString());
    }
}

AuthenticationEntryPointConfig(未登录)

@Component
public class AuthenticationEntryPointConfig implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        JSONObject json = new JSONObject();
        json.put("msg","未登录");
        httpServletResponse.setContentType("application/json;charset=utf-8");

        httpServletResponse.getWriter().write("null");
    }
}

AuthenticationFailureHandlerConfig(登录失败)

@Component
public class AuthenticationFailureHandlerConfig implements AuthenticationFailureHandler {

    @Override
    public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        JSONObject json = new JSONObject();
        json.put("msg","登录失败");
        httpServletResponse.setContentType("application/json;charset=utf-8");
        httpServletResponse.getWriter().write(json.toJSONString());
    }
}

AuthenticationSuccessHandlerConfig(登录成功)

@Component
public class AuthenticationSuccessHandlerConfig implements AuthenticationSuccessHandler {

    @Override
    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
        JSONObject json = new JSONObject();
        json.put("msg","登录成功");
        httpServletResponse.setContentType("application/json;charset=utf-8");
        httpServletResponse.getWriter().write(json.toJSONString());
    }
}

LogoutHandlerConfig(注销配置)

@Component
public class LogoutHandlerConfig implements LogoutHandler {

    @Override
    public void logout(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) {
        try {
            String url = httpServletRequest.getParameter("redirectUrl");
            //实现自定义重定向
            httpServletResponse.sendRedirect(url);
        }catch (IOException e){

        }
    }
}

注销路径:localhost:8080/logout
redirectUrl 即为前端传来自定义跳转url地址,否则注销后会自动跳转到security默认地址,如:
logoutSuccessUrl
LogoutSuccessHandlerConfig (注销成功配置)

@Component
public class LogoutSuccessHandlerConfig implements LogoutSuccessHandler {

    @Override
    public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
        JSONObject result = new JSONObject();
        result.put("msg","注销!");
        httpServletResponse.setContentType("application/json;charset=utf-8");
        httpServletResponse.getWriter().write(result.toJSONString());
    }
}
2、model准备

SysUser,SysRole,SysUserRole

@Data
public class SysUser implements Serializable {

    private String userId;

    private String userName;

    private String password;

    private String updateUserId;

    private String createUserId;

    private LocalDateTime updateTime;

    private LocalDateTime createTime;

}
@Data
public class SysRole implements Serializable {

    private String roleId;

    private String name;

    private Integer state;

    private String updateUserId;

    private String createUserId;

    private LocalDateTime updateTime;

    private LocalDateTime createTime;

}
@Data
public class SysUserRole implements Serializable {

    private String userRoleId;

    private String roleId;

    private String userId;

    private String updateUserId;

    private String createUserId;

    private LocalDateTime updateTime;

    private LocalDateTime createTime;

}

自定义校验机制原理

用户名密码 ->	(Authentication(未认证)	 ->
AuthenticationManager  -> AuthenticationProvider ->
UserDetailService -> UserDetails -> Authentication(已认证)通过
3、定义User对象,实现UserDetailsService ,SelfAuthenticationProvider类

创建 SelfUserDetails

@Data
public class SelfUserDetails implements UserDetails, Serializable {
    private String username;
    private String password;
    private Set<? extends GrantedAuthority> authorities;


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

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

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

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

创建SelfUserDetailsService,实现UserDetailsService

@Service
public class SelfUserDetailsService implements UserDetailsService {

    @Autowired
    SysUserService sysUserService;


    @Override
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
        SelfUserDetails userInfo = new SelfUserDetails();
        userInfo.setUsername(userName);
        SysUser sysUser = sysUserService.getUserByUsername(userName);
        if (sysUser == null) {
            throw new UsernameNotFoundException("用户不存在!");
        }
        userInfo.setPassword(sysUser.getPassword());
        SysRole sysRole = sysUserService.getUserRoleInfo(sysUser.getUserId());
        if (sysRole == null) {
            throw new UsernameNotFoundException("用户未分配权限!");
        }
        if (sysRole.getState() == 0) {
            throw new UsernameNotFoundException("用户权限失效!");
        }
        userInfo.setAuthorities(getAuthorities(sysRole.getName()));
        return userInfo;
    }
    private Set<GrantedAuthority> getAuthorities(String role) {
        Set<GrantedAuthority> authoritiesSet = new HashSet();
        GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(role);
        authoritiesSet.add(grantedAuthority);
        return authoritiesSet;
    }

}

创建SelfAuthenticationProvider 类,实现AuthenticationProvider

public class SelfAuthenticationProvider implements AuthenticationProvider {


    @Autowired
    SelfUserDetailsService userDetailsService;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String userName = (String) authentication.getPrincipal();
        String password = (String) authentication.getCredentials();


        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        String encodePwd = bCryptPasswordEncoder.encode(password);

        UserDetails userInfo = userDetailsService.loadUserByUsername(userName);
        if (userInfo == null) {
            throw new UsernameNotFoundException("用户不存在");
        }
        if(!userInfo.getPassword().equals(encodePwd)) {
            throw new BadCredentialsException("密码错误");
        }

        return new UsernamePasswordAuthenticationToken(userName, password, userInfo.getAuthorities());
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return UsernamePasswordAuthenticationToken.class.equals(authentication);
    }
}

4、权限配置类

创建 RbacService (role-Based-access control)权限控制,url 权限部分未实现,有兴趣的小伙伴可一起完善。

@Service("rbacService")
public class RbacServiceImpl implements RbacService {

    private AntPathMatcher antPathMatcher = new AntPathMatcher();
    
    @Override
    public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
        String userName = (String) authentication.getPrincipal();
        //读取当前用户是否拥有 url 权限
        //todo 未实现
        if (antPathMatcher.match("", request.getRequestURI())) {
            return true;
        }
        return false;
    }
}
5、配置security

创建SecurityConfig 配置类,配置核心:

 @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .httpBasic().authenticationEntryPoint(authenticationEntryPoint)
                .and()
                .authorizeRequests()
                .antMatchers("/role/**").hasRole("admin")
                // 这就表示 /index这个页面不需要权限认证,所有人都可以访问
               /* .antMatchers("/test/**").permitAll()
                .antMatchers(HttpMethod.POST,"/user/").hasRole("ADMIN")
                .antMatchers(HttpMethod.GET,"/user/").hasRole("USER")
                .antMatchers("/role/**").hasRole("admin")
                .anyRequest().authenticated()*/
                .anyRequest().access("@rbacService.hasPermission(request,authentication)")
                //.anyRequest().access("")
                // 其他 url 需要身份认证
                //.authenticated()
                .and()
                // login 相关
                // 开启登录
                .formLogin()
                // 登录成功
                .successHandler(authenticationSuccessHandler)
                // 登录失败
                .failureHandler(authenticationFailureHandler)
                .permitAll()
                .and()
                // logout 相关
                .logout()
                .logoutSuccessHandler(logoutSuccessHandler)
                .addLogoutHandler(logoutHandler)
                .permitAll().and()
                // 记住我相关
                .rememberMe()
                .rememberMeParameter("remember-me").userDetailsService(selfUserDetailsService)
                .tokenRepository(persistentTokenRepository())
                .tokenValiditySeconds(6000);
                // 无权访问 JSON 格式的数据
                http.exceptionHandling().accessDeniedHandler(accessDeniedHandler);
    }

到现在功能基本可以使用了,只需要完善一下各个状态返回result即可使用

6、记住我,记住我的功能Remeber me
	@Autowired
    DataSource dataSource;
    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
        tokenRepository.setDataSource(dataSource);
        return tokenRepository;
    }

config 中 加入

  	.rememberMe()
    .rememberMeParameter("remember-me").userDetailsService(selfUserDetailsService)
    .tokenRepository(persistentTokenRepository())
demo git地址:传送门

欢迎大家 review,完善补充,指出不足之处,谢谢。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值