SpringBoot 简单整合 SpringSecurity

avatar 2022年05月03日15:10:35 6 248 views

最近帮一个客户定制二手房屋交易系统,客户同学要求使用 Spring Security 作为安全框架。

我是在把项目完整开发完后,再改造整入 Spring Security 的。

用的也很简单,just do it

一、引入依赖

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
	<version>2.1.7.RELEASE</version>
</dependency>

 

二、User 类实现 UserDetails

添加权限集合和重写获取用户名,密码,权限的方法

package com.example.ssm.rental.entity;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.example.ssm.rental.common.base.BaseEntity;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;
import java.util.List;


/**
 * 用户信息
 *
 * @author example
 * @date 2022/4/27 12:08 下午
 */
@TableName("t_user")
@Data
public class User extends BaseEntity implements UserDetails {

    /**
     * 登录名
     */
    private String userName;

    /**
     * 姓名称
     */
    private String userDisplayName;

    /**
     * 密码
     */
    private String userPass;

    /**
     * 身份证号码
     */
    private String idCard;

    /**
     * 头像
     */
    private String userAvatar;

    /**
     * 说明
     */
    private String userDesc;

    /**
     * 0 正常
     * 1 禁用
     */
    private Integer status = 0;

    /**
     * 电子邮箱
     */
    private String email;

    /**
     * 手机号
     */
    private String phone;

    /**
     * 职业
     */
    private String job;

    /**
     * 业余爱好
     */
    private String hobby;

    /**
     * 性别
     */
    private String sex;

    /**
     * 角色:管理员admin/普通用户 user
     */
    private String role;

    /**
     * 身份证照片
     */
    private String idCardImg;

    /**
     * 未读消息
     */
    @TableField(exist = false)
    private Integer notReadMessageCount;

    /**
     * 未读消息
     */
    @TableField(exist = false)
    private String roleName;

    @TableField(exist = false)
    private List<GrantedAuthority> authorityList;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return authorityList;
    }

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

    @Override
    public String getUsername() {
        return this.userName;
    }

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

    @Override
    public boolean isAccountNonLocked() {
        return status == 0;
    }

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

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

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }


}

 

三、新建 UserDetailServiceImpl.java

重写根据用户名获取用户信息的方法

package com.example.ssm.rental.common.security;

import com.example.ssm.rental.entity.User;
import com.example.ssm.rental.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.Arrays;

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UserService userService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //根据用户名查询数据库
        User user = userService.findByUserName(username);
        if (user == null) {
            throw new UsernameNotFoundException("用户名不存在!");
        }

        GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_" + user.getRole());
        user.setAuthorityList(Arrays.asList(grantedAuthority));
        return user;
    }
}

 

 

四、新建 MyAccessDeniedHandler

自定义 403 页面

import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class MyAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        response.sendRedirect(request.getContextPath() + "/403");
    }
}

 

 

五、创建  LoginValidateAuthenticationProvider

重写身份验证的方法

package com.example.ssm.rental.common.security;

import com.example.ssm.rental.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

@Service
public class LoginValidateAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {

        //获取输入用户名
        String username = authentication.getName();

        //获取输入明文密码
        String rawPassword =(String) authentication.getCredentials();

        //根据用户名查询匹配用户
        User user = (User)userDetailsService.loadUserByUsername(username);

        //判断用户账户状态
        if (!user.isEnabled()) {
            throw new DisabledException("该账户已被禁用,请联系管理员");

        } else if (!user.isAccountNonLocked()) {
            throw new LockedException("该账号已被锁定");

        } else if (!user.isAccountNonExpired()) {
            throw new AccountExpiredException("该账号已过期,请联系管理员");

        } else if (!user.isCredentialsNonExpired()) {
            throw new CredentialsExpiredException("该账户的登录凭证已过期,请重新登录");
        }

        //验证密码
        if (!passwordEncoder.matches(rawPassword, user.getPassword())) {
            throw new BadCredentialsException("输入密码错误!");
        }
        System.out.println("认证成功!");
        return new UsernamePasswordAuthenticationToken(user, rawPassword, user.getAuthorities());
    }

    @Override
    public boolean supports(Class<?> authentication) {
        //确保authentication能转成该类
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }
}

 

六、创建 SecurityConfig 

创建 Spring Security 核心配置类

package com.example.ssm.rental.common.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    //自定义认证
    @Autowired
    private LoginValidateAuthenticationProvider loginValidateAuthenticationProvider;

    @Autowired
    private MyAccessDeniedHandler myAccessDeniedHandler;

    @Bean
    public PasswordEncoder passwordEncoder() {
        // 密码不加密
        return NoOpPasswordEncoder.getInstance();
    }


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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //配置退出操作退出
        http.logout()
                .logoutSuccessUrl("/login").permitAll();
        //配置没有权限访问跳转自定义页面
        http.exceptionHandling().accessDeniedPage("/login");
        //登录成功之后,跳转路径
        http.headers().frameOptions().disable();
        http.authorizeRequests()
                .antMatchers("/assets/**", "/favicon.ico", "/house", "/area", "/house/**").permitAll()
                .antMatchers("/admin/user", "/admin/user/**").hasRole("admin")
                .antMatchers("/admin/news", "/admin/news/**").hasRole("admin")
                .and()
                .formLogin().loginPage("/login")//登录页面
                .loginProcessingUrl("/login")//提交表单时处理登录判断操作
                .defaultSuccessUrl("/admin")
                .permitAll();
        ;//其余任何请求都需要通过验证

        // 用户没有权限的页面处理
        http.exceptionHandling()
                .accessDeniedHandler(myAccessDeniedHandler)   //配置403访问错误处理器
                .and();
        //关闭csrf跨域攻击防御
        http.csrf().disable();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //配置认证方式为自定义认证
        auth.authenticationProvider(loginValidateAuthenticationProvider);
    }


}

 

七、登录手动调用 Spring Security 的 验证 方法

这里我们不用spring security 的 默认 /login 的提交,我们自己手动去调用

登录接口代码

/**
 * 登录提交
 *
 * @param userName
 * @return
 */
@RequestMapping(value = "/login/submit", method = RequestMethod.POST)
@ResponseBody
public JsonResult loginSubmit(@RequestParam("userName") String userName,
							  @RequestParam("userPass") String userPass,
							  HttpSession session) {
	UsernamePasswordAuthenticationToken authRequest =
			new UsernamePasswordAuthenticationToken(userName, userPass);

	try {
		Authentication authentication = authenticationManager.authenticate(authRequest);
		SecurityContextHolder.getContext().setAuthentication(authentication);
		session.setAttribute("SPRING_SECURITY_CONTEXT", SecurityContextHolder.getContext()); // 这个非常重要,否则验证后将无法登陆
		User user = userService.findByUserName(userName);
		user.setNotReadMessageCount(messageService.countNotReadMessageSize(user.getId()));
		session.setAttribute(Constant.SESSION_USER_KEY, user);
	} catch (LockedException e) {
		return JsonResult.error("账号被禁用,无法登录");
	} catch (AuthenticationException e) {
		return JsonResult.error("用户名或密码错误");
	}
	// 登录成功
	return JsonResult.success("登录成功");
}

 

  • 微信
  • 交流学习,有偿服务
  • weinxin
  • 博客/Java交流群
  • 资源分享,问题解决,技术交流。群号:590480292
  • weinxin
avatar

发表评论

avatar 登录者:匿名
可以匿名评论或者登录后台评论,评论回复后会有邮件通知

  

已通过评论:0   待审核评论数:0