前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Springboot之Security前后端分离登录

Springboot之Security前后端分离登录

作者头像
海加尔金鹰
发布2020-09-16 10:01:09
9.4K3
发布2020-09-16 10:01:09
举报
文章被收录于专栏:海加尔金鹰的专栏

什么是Spring Security

Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架。它是用于保护基于Spring的应用程序的实际标准。

Spring Security是一个框架,致力于为Java应用程序提供身份验证和授权。与所有Spring项目一样,Spring Security的真正强大之处在于可以轻松扩展以满足自定义要求

官方网站:https://spring.io/projects/spring-security#learn

初识Security

因为之前已经接触过其他的权限框架:shiro 所以很好入门

比较好的入门博文:

Springboot + Spring Security 实现前后端分离登录认证及权限控制

Springboot集成SpringSecurity 附代码

关于SpringBoot应用中集成Spring Security你必须了解的那些事

Spring Boot Security

具体代码实现

通常都是通过自定义UserDetailsService, AuthenticationProvider, AuthenticationManager,UsernamePasswordAuthenticationFilter其中的一种来实现的。

最常见的是通过UserDetailsService方式来实现,方便快捷

1 引入Security包

代码语言:javascript
复制
       <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

2 创建Security配置类

代码语言:javascript
复制
/**
 * @author 海加尔金鹰 www.hjljy.cn
 * @apiNote websecurtiy权限校验处理
 * @since 2020/9/11
 **/
@Configuration
@EnableWebSecurity
@EnableGlobalAuthentication
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    /**
     * 描述:
     * http方式走 Spring Security 过滤器链,在过滤器链中,给请求放行,而web方式是不走 Spring Security 过滤器链。
     * 通常http方式用于请求的放行和限制,web方式用于放行静态资源
     **/
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                //用于配置直接放行的请求
                .antMatchers("/login").permitAll()
                //其余请求都需要验证
                .anyRequest().authenticated()
                //授权码模式需要 会弹出默认自带的登录框
                .and().httpBasic()
                //禁用跨站伪造
                .and().csrf().disable();
                //如果项目没有前后端分离,还可以通过 formlogin配置登录相关的页面和请求处理
        // 使用自定义的认证过滤器
        // http.addFilterBefore(new  MyLoginFilter(authenticationManager()),UsernamePasswordAuthenticationFilter.class);
    }

    /**
     * 描述: 静态资源放行,这里的放行,是不走 Spring Security 过滤器链
     **/
    @Override
    public void configure(WebSecurity web) {
        // 可以直接访问的静态数据
        web.ignoring()
                .antMatchers("/css/**")
                .antMatchers("/404.html")
                .antMatchers("/500.html")
                .antMatchers("/html/**")
                .antMatchers("/js/**");
    }

    /**
     * 描述:设置授权处理相关的具体类以及加密方式
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        // 设置不隐藏 未找到用户异常
        provider.setHideUserNotFoundExceptions(true);
        // 用户认证service - 查询数据库的逻辑
        provider.setUserDetailsService(userDetailsService());
        // 设置密码加密算法
        provider.setPasswordEncoder(passwordEncoder());
        auth.authenticationProvider(provider);
    }

    /**
     * 描述: 通过自定义的UserDetailsService 来实现查询数据库用户数据
     **/
    @Override
    @Bean
    protected UserDetailsService userDetailsService() {
        return new UserDetailsServiceImpl();
    }

    /**
     * 描述: 密码加密算法 BCrypt 推荐使用
     **/
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 描述: 注入AuthenticationManager管理器
     **/
    @Override
    @Bean
    public AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }
}

3 创建UserInfo类

代码语言:javascript
复制
/**
 * @author 海加尔金鹰
 * @apiNote 用户信息类 主要是提供给验证框架使用,并将用户基本信息保存到这个类
 * @since 2020/9/11
 **/
public class UserInfo extends User {

    private static final long serialVersionUID = 1L;

    /**
     * 描述: 可以添加自定义的用户属性
     * 用户邮箱
     **/
    private String email;
    /**
     * 描述: 用户ID
     **/
    private String userId;

    public UserInfo(String username, String password, Collection<? extends GrantedAuthority> authorities) {
        super(username, password, authorities);
    }
    public UserInfo(String username, String password, String userId,Collection<? extends GrantedAuthority> authorities) {
        super(username, password, authorities);
        this.userId=userId;
    }

    public UserInfo(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
        super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }
}

4 实现UserDetailsService

代码语言:javascript
复制
/**
 * @author 海加尔金鹰
 * @apiNote 用户具体验证类
 * @since 2020/9/11
 **/
public class UserDetailsServiceImpl implements UserDetailsService {
    /**
     * 这里根据传进来的用户账号进行用户信息的构建
     * 通常的做法是
     *  1 根据username查询数据库对应的用户信息
     *  2 根据用户信息查询出用户权限信息  例如菜单添加权限  sys:menu:add
     *  3 根据用户账号,密码,权限构建对应的UserDetails对象返回
     * 这里实际上是没有进行用户认证功能的,真正的验证是在UsernamePasswordAuthenticationFilter对象当中
     * UsernamePasswordAuthenticationFilter对象会自动根据前端传入的账号信息和UserDetails对象对比进行账号的验证
     * 通常情况下,已经满足常见的使用常见,不过如果有特殊需求,需要使用自己实现的具体认证方式,可以继承UsernamePasswordAuthenticationFilter对象
     * 重写attemptAuthentication 方法和successfulAuthentication方法
     * 最后在WebSecurityConfiguration里面添加自己的过滤器即可
     * @param username 用户账号
     * @return UserInfo
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //TODO 当前使用测试数据进行测试 需要修改成实际的业务逻辑处理
        //  不限制用户账号。只要密码是123456就可以通过验证 并添加权限
        String password = SecurityUtils.encryptPassword("123456");
        SimpleGrantedAuthority authority = new SimpleGrantedAuthority("sys:menu:add");
        List<GrantedAuthority> authorities =new ArrayList<>();
        authorities.add(authority);
        UserInfo userInfo =new UserInfo(username,password,authorities);
        userInfo.setEmail("hjljy@outlook.com");
        userInfo.setUserId("11111111");
        return userInfo;
    }
}

5 提供前端一个登录接口

由于项目进行前后端分离以及Security自带的登录界面不美观,所以需要提供一个登录接口给前端调用,该接口需要在配置当中放行,未授权访问需要授权的请求时,会返回401或者403状态码,前端可以根据这个进行路由提示处理

代码语言:javascript
复制
@RestController
public class LoginController {


    @Autowired
    AuthenticationManager authenticationManager;

    @PostMapping(value = "login")
    public ResultInfo login(@RequestBody Map<String,String> params)  {
        UserInfo userInfo = SecurityUtils.login(params.get("username"), params.get("password"), authenticationManager);
        return ResultInfo.success(userInfo);
    }
}

6 SecurityUtils 工具类

代码语言:javascript
复制
@Slf4j
public class SecurityUtils {

    /**
     * 描述根据账号密码进行调用security进行认证授权 主动调
     * 用AuthenticationManager的authenticate方法实现
     * 授权成功后将用户信息存入SecurityContext当中
     * @param username 用户名
     * @param password 密码
     * @param authenticationManager 认证授权管理器,
     * @see  AuthenticationManager
     * @return UserInfo  用户信息
     */
    public static UserInfo login(String username, String password, AuthenticationManager authenticationManager) throws AuthenticationException {
        //使用security框架自带的验证token生成器  也可以自定义。
        UsernamePasswordAuthenticationToken token =new UsernamePasswordAuthenticationToken(username,password );
        Authentication authenticate = authenticationManager.authenticate(token);
        SecurityContextHolder.getContext().setAuthentication(authenticate);
        UserInfo userInfo = (UserInfo) authenticate.getPrincipal();
        return userInfo;
    }

    /**
     * 获取当前登录的所有认证信息
     * @return
     */
    public static Authentication getAuthentication(){
        SecurityContext context = SecurityContextHolder.getContext();
        return context.getAuthentication();
    }

    /**
     * 获取当前登录用户信息
     * @return
     */
    public static UserInfo getUserInfo(){
        Authentication authentication = getAuthentication();
        if(authentication!=null){
            Object principal = authentication.getPrincipal();
            if(principal!=null){
                UserInfo userInfo = (UserInfo) authentication.getPrincipal();
                return userInfo;
            }
        }
        throw new BusinessException();
    }

    /**
     * 获取当前登录用户ID
     * @return
     */
    public static String getUserId(){
        UserInfo userInfo = getUserInfo();
        return userInfo.getUserId();
    }

    /**
     * 生成BCryptPasswordEncoder密码
     *
     * @param password 密码
     * @return 加密字符串
     */
    public static String encryptPassword(String password) {
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        return passwordEncoder.encode(password);
    }
}

7 登录验证

上述流程完毕之后,可以通过工具发送HTTP测试一下登录接口

代码语言:javascript
复制
POST http://localhost:8090/login
Content-Type: application/json

{
  "username": "测试账号",
  "password": 123456
}

成功返回账号信息

代码语言:javascript
复制
{
  "code": 0,
  "msg": "操作成功",
  "data": {
    "password": null,
    "username": "测试账号",
    "authorities": [
      {
        "authority": "sys:menu:add"
      }
    ],
    "accountNonExpired": true,
    "accountNonLocked": true,
    "credentialsNonExpired": true,
    "enabled": true,
    "email": "hjljy@outlook.com",
    "userId": "11111111"
  }
}

总结

总的来说入门还是很简单的,网上的资料也比较多,但是大多数的前后端分离都是自定义登录界面,不是接口分离。还有就是基本上security 会搭配oauth2使用进行权限验证。


标题:Springboot之Security前后端分离登录

作者:hjljy

地址:https://cloud.tencent.com/developer/article/1698409

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-09-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是Spring Security
  • 初识Security
  • 具体代码实现
    • 1 引入Security包
      • 2 创建Security配置类
        • 3 创建UserInfo类
          • 4 实现UserDetailsService
            • 5 提供前端一个登录接口
              • 6 SecurityUtils 工具类
                • 7 登录验证
                  • 总结
                  相关产品与服务
                  多因子身份认证
                  多因子身份认证(Multi-factor Authentication Service,MFAS)的目的是建立一个多层次的防御体系,通过结合两种或三种认证因子(基于记忆的/基于持有物的/基于生物特征的认证因子)验证访问者的身份,使系统或资源更加安全。攻击者即使破解单一因子(如口令、人脸),应用的安全依然可以得到保障。
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档