SpringSecurity

  1. 首先搭建好springboot工程,然后引入springsecurity依赖。
<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>
  1. 编写前端html页面login.html。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/login" method="post">
<input type="text" name="username">
<input type="password" name="password">
<input type="submit" value="提交">
</form>
</body>
</html>
  1. controller

    @Controller
    public class UserController{ @RequestMapping("/toLogin")
    public String toLogin()
    {
    return "login";
    }
    }
  2. 直接运行工程,访问IP+端口:http://localhost:8080/

    可以发现,这个请求被springsecurity拦截,会自动跳转到springsecurity的登录界面/login。

    所有的请求都会被重定向到它的login页面。

    这个springsecurity的登录页面的用户名默认是user,密码则是在控制台上打印的由springsecurity生成的密码:

  3. 我们输入用户名和密码后,会成功放行上一步中的请求。

  4. 那么我们如何不使用springsecurity的登录而采用我们自己的登录逻辑呢?

UserDetailService

UserDetailService是一个接口,里面只有一个方法。

UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException;

这个方法就是根据用户名来加载用户。它的入参var1就是username,username为空或者没有匹配到则抛出UsernameNotFoundException。

这个方法的返回值是UserDetails。

UserDetailService还有一些实现类,一般来说是需要自己去实现的,它也一些其他的,比如有基于内存的实现类InMemoryUserDetailsManager等等。

UserDetails

UserDetails也是一个接口,里面有各种方法。

public interface UserDetails extends Serializable {
//返回权限
Collection<? extends GrantedAuthority> getAuthorities(); String getPassword(); String getUsername(); //用户是否过期
boolean isAccountNonExpired(); //是否被锁定
boolean isAccountNonLocked();
//凭证(密码)是否过期
boolean isCredentialsNonExpired(); boolean isEnabled();
}

UserDetails就是一个用户详情对象,根据这个用户详情对象我们可以获取到一些用户的详情信息。

User

UserDetails的一个实现类就是User,它容易和项目中自己定义的User类混淆。里面有一些用户的信息。它有俩个构造:

public User(String username, String password, Collection<? extends GrantedAuthority> authorities) {
this(username, password, true, true, true, true, authorities);
} public User(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
if (username != null && !"".equals(username) && password != null) {
this.username = username;
this.password = password;
this.enabled = enabled;
this.accountNonExpired = accountNonExpired;
this.credentialsNonExpired = credentialsNonExpired;
this.accountNonLocked = accountNonLocked;
this.authorities = Collections.unmodifiableSet(sortAuthorities(authorities));
} else {
throw new IllegalArgumentException("Cannot pass null or empty values to constructor");
}
}

一个有3个参数的构造就是用户名、密码和权限。还有一个7个参数的构造,实则3个参数的构造就是调用的7个参数的构造方法。

自定义登录逻辑

创建一个UserService的实现类UserServiceImpl。实现UserDetailsService接口。

@Service
public class UserServiceImpl implements UserDetailsService { @Autowired
private PasswordEncoder passwordEncoder; @Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//根据username到数据库中查找用户
if (!"admin".equals(username))
{
throw new UsernameNotFoundException("没找到用户");
}
//找到用户就做密码匹配
//这里直接写好密码,方便验证测试。
String password = passwordEncoder.encode("123456");
//匹配成功返回用户对象UserDetails
return new User("admin",password, AuthorityUtils.
commaSeparatedStringToAuthorityList("admin,normal"));
}
}

返回值是UserDetails,是一个接口,所以选择new一个实现类User,设置权限的时候有一个工具类AuthorityUtils,里面有一个commaSeparatedStringToAuthorityList,可以把参数中的字符串分割形成不同权限。

重启工程,可以发现控制台并没有生成之前的generate security password。然后我们进入springsecurity的登录页面,就可以输入admin,123456进行登录。

这样虽然完成了自定义的登录逻辑,但是每次都是进入的springsecurity的登录页面,接下来我们需要设置自己的登录页面。

WebSecurityConfigurerAdapter

WebSecurityConfigurerAdapter是一个配置类,里面有很多默认的方法,我们需要继承写一个配置类继承它,然后重写里面的方法,然后根据需求完成相应的功能。

我们可以重写它的configure(HttpSecurity http)方法,这里是重写的HttpSecurity参数的方法。

@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
//自定义登录页面
.loginPage("/login.html");
}

里面删除父类的configure方法。这样,如果我们再访问页面http://localhost:8080/login.html,就不会存在需要springsecurity的登录页面了。如果我们要设置自己的登录页面,只需要加上http.formLogin().loginPage("/login.html");。但是这样的话我们访问所有的页面,都不会被拦截,我们只是希望没有登录的时候只能访问登录页面。因此,我们还需要加一个http.authorizeRequests().anyRequest().authenticated();

@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
//自定义登录页面
.loginPage("/login.html"); http.authorizeRequests()
//所有网页都需要认证(登录)才能访问
.anyRequest().authenticated();
}

这样我们再去访问的话会出现页面被重定向太多次从而无法访问的情况,这是因为我们把所有的页面包括登录页面也拦截了,然后又需要认证才能访问,所以会一直重定向。因此需要我们把登录页面除外。

@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
//自定义登录页面
.loginPage("/login.html"); http.authorizeRequests()
.antMatchers("/login.html").permitAll()
//所有网页都需要认证(登录)才能访问
.anyRequest().authenticated();
}

这样我们在没有登录的状态下直接访问登录完成后的首页http://localhost:8080/index.html,它会自动的跳转到http://localhost:8080/login.html。

我们输入之前自定义的登录逻辑 admin 和 123456 点击提交,发现页面不做任何操作。

这是因为我们表单提交时候的action的路径是"/login",我们需要在configure方法中配置登录的自定义登录逻辑。

我们需要添加一个.loginProcessingUrl("/login")。这个/login跟Controller中的RequestMapping没有任何的关联。

同时,我们还需要关闭csrf防护。然后设置一个登录成功的跳转页面首页index.html。

@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
//自定义登录页面
.loginPage("/login.html")
//自定义登录逻辑
.loginProcessingUrl("/login")
.successForwardUrl("/index.html"); http.authorizeRequests()
.antMatchers("/login.html").permitAll()
//所有网页都需要认证(登录)才能访问
.anyRequest().authenticated(); //关闭csrf防护
http.csrf().disable();
}

这样,我们再次输入用户名和密码之后,就可以登录了,并且会报一个错误405。说不允许method。这是因为我们.successForwardUrl("/index.html");查看源码是一个请求转发方式的跳转。

public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
request.getRequestDispatcher(this.forwardUrl).forward(request, response);
}

然而我们表单中是post。所以要修改为post方式。

在Controller中添加一个RequestMapping

@RequestMapping("/toIndex")
public String login()
{
return "index";
}

然后修改configure方法,让成功后的url到controller中去请求。

@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
//自定义登录页面
.loginPage("/login.html")
//自定义登录逻辑
.loginProcessingUrl("/login")
.successForwardUrl("/toIndex"); http.authorizeRequests()
.antMatchers("/login.html").permitAll()
//所有网页都需要认证(登录)才能访问
.anyRequest().authenticated(); //关闭csrf防护
http.csrf().disable();
}

此外我们还可以直接用重定向defaultSuccessUrl。

http.formLogin()
//自定义登录页面
.loginPage("/login.html")
//自定义登录逻辑
.loginProcessingUrl("/login")
.defaultSuccessUrl("/index.html");

SpringSecurity 小demo的更多相关文章

  1. 新手 gulp+ seajs 小demo

    首先,不说废话,它的介绍和作者就不在多说了,网上一百度一大堆: 我在这里只是来写写我这2天抽空对seajs的了解并爬过的坑,和实现的一个小demo(纯属为了实现,高手请绕道); 一.环境工具及安装 1 ...

  2. Nancy之基于Nancy.Hosting.Self的小Demo

    继昨天的Nancy之基于Nancy.Hosting.Aspnet的小Demo后, 今天来做个基于Nancy.Hosting.Self的小Demo. 关于Self Hosting Nancy,官方文档的 ...

  3. Nancy之基于Nancy.Owin的小Demo

    前面做了基于Nancy.Hosting.Aspnet和Nancy.Hosting.Self的小Demo 今天我们来做个基于Nancy.Owin的小Demo 开始之前我们来说说什么是Owin和Katan ...

  4. Nancy之基于Self Hosting的补充小Demo

    前面把Hosting Nancy with ASP.NET.Self Hosting Nancy和Hosting Nancy with OWIN 以demo的形式简单描述了一下. 这篇是为Self H ...

  5. [Unity3D]做个小Demo学习Input.touches

    [Unity3D]做个小Demo学习Input.touches 学不如做,下面用一个简单的Demo展示的Input.touches各项字段,有图有真相. 本项目已发布到Github,地址在(https ...

  6. Android -- 自定义View小Demo,动态画圆(一)

    1,转载:(http://blog.csdn.NET/lmj623565791/article/details/24500107),现在如下图的效果: 由上面的效果图可以看到其实是一个在一个圆上换不同 ...

  7. Win10 FaceAPI小demo开发问题汇总

    Win10 FaceAPI小demo开发问题汇总 最近使用微软牛津计划做一个小demo,使用FaceAPI做一个小应用,实现刷脸的功能.开发的过程中用到几个问题,具体如下: Stream 与IRand ...

  8. 模仿京东顶部搜索条效果制作的一个小demo

    最近模仿京东顶部搜索条效果制作的一个小demo,特贴到这里,今后如果有用到可以参考一下,代码如下 #define kScreenWidth [UIScreen mainScreen].bounds.s ...

  9. Android学习小Demo一个显示行线的自定义EditText

    今天在处理一个EditText的时候,想着把EditText做成像一本作业本上的纸一样,每一行都可以由线条隔开,具体效果如下: 1)最开始的思路 一开始的想法是很简单的,找出每一行的高度,然后一行一行 ...

随机推荐

  1. 免费 CDN 玩法 —— 文件一键上传到 NPM

    前言 unpkg.jsdelivr 等站点可加速 NPM 包文件,适合作为个人网站或演示案例的免费 CDN. 虽然上传文件到 NPM 很简单,创建 package.json 然后 npm publis ...

  2. IEEE754浮点数的转换

    将十进制数转换为单精度浮点数 如何将十进制数转换为单精度浮点数参考 首先要知道 IEEE浮点标准:V=(-1)^s * M * 2^E 1.符号(sign)s决定这个数是负数(s=1)还是正数,0(s ...

  3. Microsoft Porject Online 学习随手记一:环境创建和数据导入

    没有想像的简单,也没那么复杂 Project OL之前是Dynamics 365 Enterprise P1中的一个模块,目前最新版本只能简单创建并且已经没有Enterprise P1选项. 主要流程 ...

  4. Convolutional Neural Network-week1编程题(TensorFlow实现手势数字识别)

    1. TensorFlow model import math import numpy as np import h5py import matplotlib.pyplot as plt impor ...

  5. 野指针和free总结超有用的资料

    在C语言项目中,经常会遇到需要程序员手动分配内存的地方.这样做能够节省大量的内存空间,也让程序更加灵活.只要你有一定的基础,那么肯定用过 malloc 或者 ralloc和free的组合.这个组合使用 ...

  6. pascals-triangle leetcode C++

    Given numRows, generate the first numRows of Pascal's triangle. For example, given numRows = 5, Retu ...

  7. cf Learn from Life (简单贪心)

    有N个人站在一楼.一个电梯最多承载K个人. 每个人都有一个想去的楼层.f[1]....f[N]. f[i]属于[2,2000] 从a层到b层需花费abs(a-b)秒. 问电梯送完所有人然后回到一楼至少 ...

  8. 挂载iscsi存储

    参考连接:https://segmentfault.com/a/1190000005853387?utm_source=tag-newest 安装客户端工具,iscsi-initiator yum i ...

  9. LOTO虚拟示波器软件功能演示之——FIR数字滤波

    本文章介绍一下LOTO示波器新出的功能--FIR数字滤波的功能. 在此之前我们先来了解一下带通滤波和带阻滤波.我们都知道每个信号是不同频率不同幅值正弦波的线性叠加,为了方便直接得观察到这种现象,就有了 ...

  10. 『学了就忘』Linux基础命令 — 22、Linux中的硬链接和软链接

    目录 1.文件和目录的基本存储 2.In命令介绍 (1)我们来看看ln命令的基本信息 (2)ln命令的基本格式 3.创建硬链接 (1)如何创建硬链接 (2)硬链接特征 (3)硬连接原理 4.创建软链接 ...