注:之前写过一些列的SpringSecurity的文章,重新写一遍是为了把某些不必要的步骤省去,留下精简的,因为工作中有一些不需要。

在java的权限框架里,shiro和SpringSecurity是用的最多的。随着springboot的流行,SpringSecurity也越来越火了,因为springboot默认支持SpringSecurity。所以很有必要把SpringSecurity也搞明白。shiro更加轻量级,SpringSecurity的功能更加丰富。

软件环境:

  开发工具:Idea

  JDK:1.8

  maven:3.3.9

* SpringBoot版本:1.5.18  (说明:springboot版本没有用最新版的2.0x,因为2.0x和1.5 差别比较大吧,2.x好似是基于spring5的,SpringSecurity的更新速度好似跟不上springboot,所以这里如果用springboot 2.x会有问题,保险起见这里就不求新了)

SpringSecurity核心功能:认证、授权、攻击防护(防止伪造身份)

话不多说,下边开始打代码。

一、项目搭建

1.1  热身 ,SpringSecurity默认的Httpbasic鉴权

1.1.1,新建项目

填好项目信息,next

选启动器:SpringSecurity、Web

设置项目目录。finish

建好后的项目结构:security包放有关SpringSecurity的代码

maven依赖:

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

引入了SpringSecurity的starter后,会自动引入SpringSecurity相关的相关依赖:

 1.1.2 访问我们的服务

新建一个Controller:

@RestController
public class UserController { @RequestMapping("/hello")
public String hello(){
return "Hello ! ";
}
}

启动项目,访问localhost:8080/hello,默认就会出来这个登录框,只要引入了SpringSecurity,springboot就默认会启用http basic认证,所有的服务都会被保护起来, ,。

其中的默认用户名是user,默认的密码在启动项目的时候会在控制台打印。

输入用户名密码,就能访问:

如果输入的用户名或密码错误,Springboot会引导你到一个空白页,这个也是可以配置的,后边再说怎么配置:

你还可以关闭这个默认的鉴权,只需在application.properties 里配置:  security.basic.enabled = false ,这个值默认是true。

二、表单登录

如果想去掉那个比较丑的basic登录框,只需要一个配置类即可。新建配置类 BrowserSecurityConfig,继承  WebSecurityConfigurerAdapter

@EnableWebSecurity
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter { }

(为什么这么写?因为SpringSecurity官方文档这么说的https://docs.spring.io/spring-security/site/docs/4.2.10.RELEASE/reference/htmlsingle/#samples):

    5.1 Hello Web Security Java配置

    第一步是创建Spring Security Java配置。配置创建一个Servlet过滤器,称为springSecurityFilterChain负责应用程序内所有安全性(保护应用程序URL,验证提交的用户名和密码,重定向到登录表单等)。您可以在下面找到Spring Security Java配置的最基本示例:

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

import org.springframework.context.annotation.*;
import org.springframework.security.config.annotation.authentication.builders.*;
import org.springframework.security.config.annotation.web.configuration.*; @EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Bean
public UserDetailsService userDetailsService() throws Exception {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("user").password("password").roles("USER").build());
return manager;
}
}
然后启动application,访问http://localhost:8080/hello,可以看到已经出来了一个登录页面,输入用户名user,以及控制台打印的密码:

登录后 访问的是登录之前的要访问的hello服务:

官网说明:

到目前为止,我们的WebSecurityConfig仅包含有关如何验证用户身份的信息。Spring Security如何知道我们要求所有用户进行身份验证?Spring Security如何知道我们想要支持基于表单的身份验证?原因是WebSecurityConfigurerAdapterconfigure(HttpSecurity http)方法中提供了一个默认配置,如下所示:

protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.httpBasic();
}

三、自定义用户认证逻辑

3.1 ,处理用户信息获取 、用户校验  、密码的加密解密

新建一个类,MyUserDetailService (这个类必须是Spring里的一个Bean,所以加上@Component注解)实现 UserDetailsService 接口,UserDetailsService 接口只有一个方法:通过用户名查询用户信息,返回UserDetail

UserDetailsService.java 
public interface UserDetailsService {
UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException;
}
UserDetails.java : 提供了几个方法,账户是否启用、账户是否过期、密码是否过期、账户是否锁定、权限集合信息
public interface UserDetails extends Serializable {
Collection<? extends GrantedAuthority> getAuthorities(); String getPassword(); String getUsername(); boolean isAccountNonExpired(); boolean isAccountNonLocked(); boolean isCredentialsNonExpired(); boolean isEnabled();
}

配置一个密码的加密解密器类:PasswordEncoder,我们用其一个实现类 BCryptPasswordEncoder,只有两个方法,一个是加密密码,一个是匹配方法。如果已有系统已经有了自己的加密算法,这里可以换成自己的加解密逻辑。

  @Bean
public PasswordEncoder passwordencoder(){
//BCryptPasswordEncoder implements PasswordEncoder
return new BCryptPasswordEncoder();
}

这个接口的实现类会给加密的密码随机加盐,所以一样的密码每次加密出来是不一样的,更安全。如123456加密2次:

加密后密码:  $2a$10$BChH.C4.X8MYuI1mHFoOkefWhOsad7SvhZedHFt1OG4vjSu.z9weC

加密后密码:  $2a$10$YUbz.miE5C0aAcuU1FnHSu/U.Qm/BujTNw6X7S5i4/6AhjyDc6suK

此时我们的逻辑是,只要密码输入123456,就能登录成功。启动项目试验:访问/hello ,会自动跳转/login ,随便输入用户名aaaaaa,输入密码123456 ,成功访问Hello!

    |     

3.2 自定义登录页面

用SpringSecurity提供的默认的登录页肯定是不行的,所以需要自定义登录页(当然这里还可以配置成一个Controller方法,然后跳转到登录页。或者从配置文件里读取),在BrowserSecurityConfig类里配置:

@Override
protected void configure(HttpSecurity http) throws Exception {
//http.httpBasic() //这个就是默认的弹框认证
http.formLogin() //表单认证
.loginPage("/login.html")
.loginProcessingUrl("/authentication/form")
.and()
.authorizeRequests() //下边的都是授权的配置
.antMatchers("/login.html").permitAll() //放过登录页不过滤
.anyRequest() //任何请求
.authenticated(); //都需要身份认证
}
.loginPage("/login.html") 配置登录页面
.loginProcessingUrl("/authentication/form") 配置处理登录表单的action,这个值默认在UsernamePasswordAuthenticationFilter 类里,

注意一定要放过登录页不过滤,否则一直不能跳转到login.html

此时访问 /hello ,跳转到自定义的登录页:

随便输入用户名,密码输入123456 ,登录,出现

SpringSecurity默认提供了CSRF(跨站请求伪造)防护,是用CSRF token来完成防护的。暂时关闭CSRF防护,在BrowserSecurityConfig里设置:

.csrf().disable();
再次登录,可以成功访问到 /hello服务。
 至此自定义登录页面完成吗,下一步自定义登录成功处理。
也可以把登录页做成在配置文件application.properties里配置,然后在代码里读取配置,这样更灵活、更通用。
这里有springboot读取配置文件的用法:https://www.cnblogs.com/lihaoyang/p/10223339.html

3.2 自定义登录成功/失败处理

现在的流程是,访问一个需要登录的服务,如果没有登录,跳转登录页,等你登录后,立马就访问登录之前的服务。如果你想在登录成功后做一些处理,比如签到,送积分等等。那就需要自定义登录成功的处理。如果没有这种需求,这一步骤可以略过。

springsecurity提供了一个接口,AuthenticationSuccessHandler,用来处理登录成功后的逻辑:

/**
* Strategy used to handle a successful user authentication.
* <p>
* Implementations can do whatever they want but typical behaviour would be to control the
* navigation to the subsequent destination (using a redirect or a forward). For example,
* after a user has logged in by submitting a login form, the application needs to decide
* where they should be redirected to afterwards (see
* {@link AbstractAuthenticationProcessingFilter} and subclasses). Other logic may also be
* included if required.
*
* @author Luke Taylor
* @since 3.0
*/
public interface AuthenticationSuccessHandler { /**
* Called when a user has been successfully authenticated.
*
* @param request the request which caused the successful authentication
* @param response the response
* @param authentication the <tt>Authentication</tt> object which was created during
* the authentication process.
*/
void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws IOException, ServletException; }

1,定义一个自己的成功处理器,实现这个Handler:

package com.lhy.browser.security.authentication;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException; /**
* 认证成功处理器
*/
@Component("myAuthenticationSuccessHandler")
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler { private Logger logger = LoggerFactory.getLogger(getClass()); //springmvc启动会自动注册一个ObjectMapper
@Autowired
private ObjectMapper objectMapper; @Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { logger.info("登录成功");
//把authentication返回给响应
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(objectMapper.writeValueAsString(authentication)); }
}

2、添加到配置类,让spring security执行自定义的处理器

在 BrowserSecurityConfig  配置类里注入 AuthenticationSuccessHandler

@EnableWebSecurity
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter { //自定义的认证成功的处理器
@Autowired
private AuthenticationSuccessHandler myAuthenticationSuccessHandler;
//自定义的认证失败的处理器
@Autowired
private AuthenticationFailureHandler myAuthenticationFailureHandler; @Autowired
private SecurityProperties securityProperties; @Override
protected void configure(HttpSecurity http) throws Exception {
//http.httpBasic() //这个就是默认的弹框认证
http.formLogin() //表单认证
.loginPage(securityProperties.getBrowser().getLoginPage())//登录页
.loginProcessingUrl("/authentication/form") //登录提交action
.successHandler(myAuthenticationSuccessHandler) //自定义的认证成功处理器
.failureHandler(myAuthenticationFailureHandler)//自定义的认证失败的处理器
.and()
.authorizeRequests() //下边的都是授权的配置
.antMatchers(securityProperties.getBrowser().getLoginPage()).permitAll() //放过登录页不过滤
.anyRequest() //任何请求
.authenticated() //都需要身份认证
.and()
.csrf().disable();
} }

登录失败处理器和登录成功类似,自定义失败处理类,实现  AuthenticationFailureHandler  接口即可:

/**
* 认证失败处理器
*/
@Component("/myAuthenticationFailureHandler")
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler { private Logger logger = LoggerFactory.getLogger(getClass()); //springmvc启动会自动注册一个ObjectMapper
@Autowired
private ObjectMapper objectMapper; @Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
logger.info("登录失败"); //把authentication返回给响应
//状态码500,服务器内部错误
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(objectMapper.writeValueAsString(exception.getMessage()));
}
}

这样就可以在登录成功和失败后进入自己的处理逻辑了。

用SpringSecurity从零搭建pc项目-01的更多相关文章

  1. 用SpringSecurity从零搭建pc项目-02

    参照这一篇文章吧,比如你不需要做的那么通用,取其中一部分代码即可. https://www.cnblogs.com/lihaoyang/p/8491792.html

  2. 腾讯云centos7 从零搭建laravel项目

    目标,访问网站出现: -----------------------分割线---------------------------------------- 一.Laravel Homestead 环境 ...

  3. SpringBoot整合SpringSecurity简单实现登入登出从零搭建

    技术栈 : SpringBoot + SpringSecurity + jpa + freemark ,完整项目地址 : https://github.com/EalenXie/spring-secu ...

  4. 零配置简单搭建SpringMVC 项目

    SpringMVC是比较常用的JavaWeb框架,非常轻便强悍,能简化Web开发,大大提高开发效率,在各种Web程序中广泛应用.本文采用Java Config的方式搭建SpringMVC项目,并对Sp ...

  5. 从零搭建一个SpringCloud项目之Feign搭建

    从零搭建一个SpringCloud项目之Feign搭建 工程简述 目的:实现trade服务通过feign调用user服务的功能.因为trade服务会用到user里的一些类和接口,所以抽出了其他服务需要 ...

  6. 从零搭建一个IdentityServer——项目搭建

    本篇文章是基于ASP.NET CORE 5.0以及IdentityServer4的IdentityServer搭建,为什么要从零搭建呢?IdentityServer4本身就有很多模板可以直接生成一个可 ...

  7. 从零搭建基于webpack的Electron-Vue3项目(1)——基于webpack的Vue3项目搭建

    从零搭建基于webpack的Electron-Vue3项目(1)--基于webpack的Vue3项目搭建 前言 本篇文章内容,主要是基于webpack的Vue3项目开发环境进行搭建,暂时还不涉及到El ...

  8. 从零搭建react hooks项目(github有源代码)

    前言 首先这是一个react17的项目,包含项目中常用的路由.状态管理.less及全局变量配置.UI等等一系列的功能,开箱即用,是为了以后启动项目方便,特地做的基础框架,在这里分享出来. 这里写一下背 ...

  9. 01.基于IDEA+Spring+Maven搭建测试项目--综述

    目前公司的测试工作中常见两种接口:HTTP和Dubbo,这两种接口类型均可以使用相关测试工具进行测试,但都会有一定的局限性和不便之处,具体如下: 1.HTTP接口,当需要对于参数进行加密解密时,就得对 ...

随机推荐

  1. 代码的二次重构(开篇:JDBC连接数据库)

    Java中使用JDBC连接数据库时,若是使用初级的代码,代码复用率非常低,连接过程简单来说分为以下几个步骤: 加载驱动包 准备好URL链接获取数据库连接(driver和url根据不同的数据库的不同而不 ...

  2. 程序重复报more than 'max_user_connections' active connections问题解决

    早晨,开发扔过来一个问题,截图如下: ums already has more than 'max_user_connections' active connections 查看数据库发现: 最大连接 ...

  3. c语言结构体链表

    原文链接:http://zhina123.blog.163.com/blog/static/417895782012106036289/ 引用自身的结构体,一个结构体中有一个或多个成员的基类型就是本结 ...

  4. C++类、继承、多态、虚函数

    一个比较好的虚函数例子 /****************************/ /* 作者:骆天 */ /* 时间:2018/1/26 */ /* 代码:多态的理解 */ /********** ...

  5. 2.2.2synchronized同步代码块的使用

    当两个并发线程访问同一个对象object中的synchronized(this)同步代码块时,一段时间内只能有一个线程执行,另一个线程必须等待期执行完才能执行. package com.cky.bea ...

  6. OkHttp3几个简单的例子和在子线程更新UI线程的方法

    okHttp用于android的http请求.据说很厉害,我们来一起尝尝鲜.但是使用okHttp也会有一些小坑,后面会讲到如何掉进坑里并爬出来. 首先需要了解一点,这里说的UI线程和主线程是一回事儿. ...

  7. mach_absolute_time 使用

    今天看荣哥时间常用函数封装里有个不常见的函数 ,mach_absolute_time() ,经查询后感觉是个不错的函数,网上关于这个函数搜索以后简单整理来一下. 什么事Mach? 时间例程依赖于所需要 ...

  8. Alpha阶段敏捷冲刺(四)

    1.站立式会议 提供当天站立式会议照片一张 2.每个人的工作 (有work item 的ID),并将其记录在码云项目管理中: 昨天已完成的工作. 祁泽文:实现了统计的基本按钮和界面. 徐璐琳:找到&q ...

  9. Beta阶段第一篇 Scrum 冲刺博客

    介绍小组新加入的成员,Ta担任的角色 新成员 担任角色 张晨晨 测试 理由:晨晨代码能力有待提高,但心思细腻有耐心,适合测试工作. 讨论是否需要更换团队的PM 通过团队讨论决定不更换团队PM,理由是在 ...

  10. LED点阵书写显示

    LED点阵书写显示屏   摘要:随着时代的发展,数字电子技术已经普及到我们生活,工作,科研,各个领域,而LED显示以其组构方式灵活.显示稳定.功耗低.寿命长.技术成熟.成本低廉等特点在车站.证券所.运 ...