SpringSecurity 小demo
SpringSecurity
- 首先搭建好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>
- 编写前端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>
controller
@Controller
public class UserController{ @RequestMapping("/toLogin")
public String toLogin()
{
return "login";
}
}
直接运行工程,访问IP+端口:http://localhost:8080/
可以发现,这个请求被springsecurity拦截,会自动跳转到springsecurity的登录界面/login。
所有的请求都会被重定向到它的login页面。

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

我们输入用户名和密码后,会成功放行上一步中的请求。
那么我们如何不使用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的更多相关文章
- 新手 gulp+ seajs 小demo
首先,不说废话,它的介绍和作者就不在多说了,网上一百度一大堆: 我在这里只是来写写我这2天抽空对seajs的了解并爬过的坑,和实现的一个小demo(纯属为了实现,高手请绕道); 一.环境工具及安装 1 ...
- Nancy之基于Nancy.Hosting.Self的小Demo
继昨天的Nancy之基于Nancy.Hosting.Aspnet的小Demo后, 今天来做个基于Nancy.Hosting.Self的小Demo. 关于Self Hosting Nancy,官方文档的 ...
- Nancy之基于Nancy.Owin的小Demo
前面做了基于Nancy.Hosting.Aspnet和Nancy.Hosting.Self的小Demo 今天我们来做个基于Nancy.Owin的小Demo 开始之前我们来说说什么是Owin和Katan ...
- Nancy之基于Self Hosting的补充小Demo
前面把Hosting Nancy with ASP.NET.Self Hosting Nancy和Hosting Nancy with OWIN 以demo的形式简单描述了一下. 这篇是为Self H ...
- [Unity3D]做个小Demo学习Input.touches
[Unity3D]做个小Demo学习Input.touches 学不如做,下面用一个简单的Demo展示的Input.touches各项字段,有图有真相. 本项目已发布到Github,地址在(https ...
- Android -- 自定义View小Demo,动态画圆(一)
1,转载:(http://blog.csdn.NET/lmj623565791/article/details/24500107),现在如下图的效果: 由上面的效果图可以看到其实是一个在一个圆上换不同 ...
- Win10 FaceAPI小demo开发问题汇总
Win10 FaceAPI小demo开发问题汇总 最近使用微软牛津计划做一个小demo,使用FaceAPI做一个小应用,实现刷脸的功能.开发的过程中用到几个问题,具体如下: Stream 与IRand ...
- 模仿京东顶部搜索条效果制作的一个小demo
最近模仿京东顶部搜索条效果制作的一个小demo,特贴到这里,今后如果有用到可以参考一下,代码如下 #define kScreenWidth [UIScreen mainScreen].bounds.s ...
- Android学习小Demo一个显示行线的自定义EditText
今天在处理一个EditText的时候,想着把EditText做成像一本作业本上的纸一样,每一行都可以由线条隔开,具体效果如下: 1)最开始的思路 一开始的想法是很简单的,找出每一行的高度,然后一行一行 ...
随机推荐
- 并发编程从零开始(六)-BlockingDeque+CopyOnWrite
并发编程从零开始(六)-BlockingDeque+CopyOnWrite 5.2 BlockingDeque BlockingDeque定义了一个阻塞的双端队列接口: 该接口继承了BlockingQ ...
- NOIP模拟84(多校17)
T1 宝藏 解题思路 考场上一眼出 \(nlog^2\) 做法,然后没看见是 1s 3e5 的数据,我竟然以为自己切了?? 考完之后尝试着把二分改为指针的移动,然后就过了??或许是数据水吧,感觉自己的 ...
- 【学习笔记】Vizing 定理
图染色问题的经典结论 定义 称一个边染色方案合法当且仅当每个顶点连出的所有边的颜色都互不相同,如果此时出现了 \(k\) 个颜色那么称该方案是图的一组 \(k\) 染色 一张无向图的边着色数为最小的 ...
- 数据治理之元数据管理的利器——Atlas入门宝典
随着数字化转型的工作推进,数据治理的工作已经被越来越多的公司提上了日程.作为Hadoop生态最紧密的元数据管理与发现工具,Atlas在其中扮演着重要的位置.但是其官方文档不是很丰富,也不够详细.所以整 ...
- 手把手搭建自己的智能家居 - 基于 IOT Pi 的智能甲醛检测器
智慧家居 - 基于 IOT Pi 的智能甲醛检测器 之前的文章体验 MS-RTOS 的时候入手了一个块 IOT Pi ,放着也是浪费,这次我们就利用 IOT PI 开发一个智能甲醛检测器.φ(> ...
- Linux调整时区和同步时间
1.调整时区 tzselect 选择Asia -> China -> Beijing Time 2.设置为默认时区 cp -f /usr/share/zoneinfo/Asia/Shang ...
- Python import commands ImportError: No module named 'commands'
ImportError: No module named 'commands' 在Python3中执行shell脚本,想要获取其执行状态和标准输出.错误输出 的数据,遇到这个错误,原因是command ...
- sed tr 批量转换邮箱格式 去除"\n" 行尾添加";"
1:从phpmyadmin上拿下来的数据是这样的: 2:od -c 发现存在\r\n (windows上编码问题) $ od -c sql.csv 先将\r处理掉 $ sed 's/\r//' sql ...
- k8s入坑之路(13)kubernetes重要资源(namespace隔离 resources资源管理 label)
Namespace --- 集群的共享与隔离 语言中namespace概念 namespace核心作用隔离 以上是隔离的代码.namespace隔离的是: 1.资源对象的隔离:Service.Depl ...
- Git 图形化客户端--Sourcetree
1.图形化客户端: sourcetre下载:https://www.sourcetreeapp.com/ 2.接着并执行SourceTreeSetup-3.1.3.exe,会进入登录或注册bitbuc ...