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)最开始的思路 一开始的想法是很简单的,找出每一行的高度,然后一行一行 ...
随机推荐
- 一次简单的SQL注入绕WAF
本人也是小白一枚,大佬请绕过,这个其实是六月份的时候做的,那时候想多点实战经验,就直接用谷歌搜索找了一些网站,这个是其中一个 1.目标网站 2.发现有WAF防护 3.判断存在注入 4.猜测了一下闭合为 ...
- Vue CLI 5 和 vite 创建 vue3.x 项目以及 Vue CLI 和 vite 的区别
这几天进入 Vue CLI 官网,发现不能选择 Vue CLI 的版本,也就是说查不到 vue-cli 4 以下版本的文档. 如果此时电脑上安装了 Vue CLI,那么旧版安装的 vue 项目很可能会 ...
- cs224n 2019
视频链接 相关资源 Notes 笔记下载 笔记2 需要挂梯子,不然不显示图片,如果用ssr,要调到全局模式 转自:bitJoy CS224N(1.8)introduction and Word Vec ...
- 微信小程序 scroll-view 完成上拉加载更多
我们经常在软件客户端上看到这么一个功能,当我们阅读信息浏览到文章的末尾时,通常会加载出更多的信息.比如,我们在简书客户端上浏览推荐文章时,浏览到屏幕的末尾,此时又加载出了另一页的推荐文章,即实现了上拉 ...
- Spring 5 中函数式web开发中的swagger文档
Spring 5 中一个非常重要的更新就是增加了响应式web开发WebFlux,并且推荐使用函数式风格(RouterFunction和 HandlerFunction)来开发WebFlux.对于之前主 ...
- 用建造者模式实现一个防SQL注入的ORM框架
本文节选自<设计模式就该这样学> 1 建造者模式的链式写法 以构建一门课程为例,一个完整的课程由PPT课件.回放视频.课堂笔记.课后作业组成,但是这些内容的设置顺序可以随意调整,我们用建造 ...
- Noip模拟67 2021.10.3
还是困,不过已经可以用脑子思考问题了 T1 数据恢复 没啥明确的算法,可以说是贪心? 考虑部分分, 链的直接扫, 对于菊花的发现只要根节点在第一个,剩下的点位置不重要 那么按照$a/b$排序,扫一遍就 ...
- 【做题记录】[NOI2008] 假面舞会—有向图上的环与最长链
luogu 1477 [NOI2008] 假面舞会 容易发现: 如果图中没有环,那么面具种数一定是所有联通块内最长链之和,最少为 \(3\) . 如果有环,则面具种数一定是所有环的大小的最大公约数. ...
- Java之父 James Gosling 发表博文 《Too Soon》纪念乔布斯。
几个礼拜前,我们还在讨论乔布斯的辞职.虽然我们都知道这意味着什么,但是我没有想到一切来的如此之快.已经有很多关于这件事情的文章了,特别是"经济学人"的这篇文章. 乔布斯是一个很独特 ...
- diff 命令,防止遗忘
常规输出: diff 1.file 2.file 并排格式输出: diff 1.file 2.file -y -W 50 显示说明 "|"表示前后2个文件内容有不同 "& ...