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. Frida高级逆向-Hook Native(Java So)2

    Frida Hook So 一些操作说明 Native方法第一个参数是 JNIEnv *env 如何在Frida中获取 JNIEnv 对象呢? Java.vm.getEnv(); 如何将string类 ...

  2. C++ 类继承 笔记(初步)

    本节内容源于对C++ primer第13章的学习,这本书把C++的原理将得明明白白.网上的博客往往讲得一头雾水.到头来还不如看原书本. 问题 首先给出一题: #include<stdio.h&g ...

  3. python之字符串,列表,集合,字典方法

    字典内置函数&方法 函数: 1.len(dict1):打印字典的键的个数 方法:dict1.( ) 2.clear():清空字典 3.copy():复制字典 4.fromkeys():使用指定 ...

  4. Pytorch——张量 Tensors

    张量 Tensors 1.torch.is_tensor torch.is_tensor(obj) 用法:判断是否为张量,如果是 pytorch 张量,则返回 True. 参数:obj (Object ...

  5. 在hive中使用COALESCE进行空值处理

    COALESCE (expression_1, expression_2, ...,expression_n)依次参考各参数表达式,遇到非null值即停止并返回该值.如果所有的表达式都是空值,最终将返 ...

  6. [对对子队]会议记录5.15(Scrum Meeting2)

    今天已完成的工作 吴昭邦 ​ 工作内容:衔接循环指令系统,搭建第4关 ​ 相关issue:实现循环组件 ​ 相关签入:feat: 将模型加入第四关 第四关可以顺利通过 何瑞 ​ 工作内容:衔接循环指令 ...

  7. 【二食堂】Alpha - Scrum Meeting 4

    Scrum Meeting 4 例会时间:4.14 12:30 - 12:50 进度情况 组员 昨日进度 今日任务 李健 1. 主页面的搭建工作issue 1. 完成主页搭建**issue2. 与后端 ...

  8. 有了 HTTP 协议,为什么还需要 Websocket?

    WebSocket 是一种基于 TCP 连接上进行全双工通信的协议,相对于 HTTP 这种非持久的协议来说,WebSocket 是一个持久化网络通信的协议. 它不仅可以实现客户端请求服务器,同时可以允 ...

  9. poj 3041 Asteroids(最小点覆盖)

    题意: N*N的矩阵,有K个敌人,坐标分别是(C1,C1),.....,(Rk,Ck). 有一个武器,每发射一次,可消掉某行或某列上的所有的敌人. 问消灭所有敌人最少需要多少发. 思路: 二分建图:左 ...

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

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