准备条件
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--springsecurity整合thymeleaf的包-->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
@Controller
public class RouterController {
@RequestMapping({"/", "/index"})
public String toIndex() {
return "index";
}
@RequestMapping("/toLogin")
public String toLogin() {
return "views/login";
}
@RequestMapping("/level1/{id}")
public String level1(@PathVariable("id") Integer id) {
return "views/level1/" + id;
}
@RequestMapping("/level2/{id}")
public String level2(@PathVariable("id") Integer id) {
return "views/level2/" + id;
}
@RequestMapping("/level3/{id}")
public String level3(@PathVariable("id") Integer id) {
return "views/level3/" + id;
}
}
1、启动项目在控制台会打印如下信息
此时在浏览器输入http://localhost:8001,会跳转到登录页面:
默认用户名为user,密码就是控制台打印的。
以上信息说明SpringSecurity生效了!正确输入上面的用户和密码即可登陆将进入首页!
2、SpringSecurity简介
3、以Aop横切的方式配置javaConfig继承WebSecurityConfigurerAdapter,使用@EnableWebSecurity开启此功能,并且重写protected void configure(HttpSecurity http) throws Exception
方法
授权操作
@Override
protected void configure(HttpSecurity http) throws Exception {
//授权
//首页所有的人都可以访问,功能页只有对应有权限的人才可以访问
//请求授权的规则
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/level1/**").hasRole("vip1")
.antMatchers("/level2/**").hasRole("vip2")
.antMatchers("/level3/**").hasRole("vip3");
}
登陆ip地址http://localhost:8001即可直接成功访问首页
随便点击一个链接,出现禁止访问的提示!
在配置类中的方法protected void configure(HttpSecurity http) throws Exception添加“如果没有权限访问就到登陆页面”
这个登陆页面是SpringSecurity自定义的
//没有权限默认会到登陆页面
http.formLogin();
在配置类中添加认证操作的方法protected void configure(AuthenticationManagerBuilder auth) throws Exception
//认证
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//这些数据正常是从数据库中读出的
//从内存中认证,也可以从数据库中查询相应角色的数据进行认证
auth.inMemoryAuthentication()
.withUser("ml").password("123456").roles("vip1","vip2")
.and()
.withUser("root").password("123").roles("vip1","vip2","vip3")
.and()
.withUser("guest").password("1").roles("vip1");
}
登陆iphttp://localhost:8001/访问首页,点击level1下的链接,使用
登陆
出现下面的错误!
解决方法
,修改上面的配置类(原因是SpringSecurity认为使用.withUser("ml").password("123456").roles("vip1","vip2")。设置密码太简单,如果使用反编译,密码是极为不方便的。所以将明文加密防止反编译
)
配置类修改如下
//认证
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//这些数据正常是从数据库中读出的
//从内存中认证,也可以从数据库中查询相应角色的数据进行认证
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("ml").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2")
.and()
.withUser("root").password(new BCryptPasswordEncoder().encode("123")).roles("vip1","vip2","vip3")
.and()
.withUser("guest").password(new BCryptPasswordEncoder().encode("12")).roles("vip1");
}
重启项目,使用下面的账号、密码登陆测试!
可以看到使用ml、123456登陆可以访问vip1和vip2下的.antMatchers("/level1/**").hasRole("vip1") .antMatchers("/level2/**").hasRole("vip2")
但是使用ml、123456登陆却不可以访问 .antMatchers("/level3/**").hasRole("vip3")
4、注销及权限控制
(1)在配置类中的方法protected void configure(HttpSecurity http) throws Exception
添加注销功能
//开启注销功能,跳到到首页
http.logout();
上面的toLogin是@Controller中请求的路径(请求登陆),而logout不是我们自己写的@Controller中的请求,是根据http.logout点击进去的源码得来的,说明源码中已经给我们定义好了注销功能的路径,不用我们再自己写
即可使用该密码、账号访问对应的页面!
点击右上角的“注销”功能!
点击退出到登陆页面
当然也可以直接使用我们自己定义好的路由直接退出到首页。修改上面的视图(这个是错误的,上面是正确的的。原因在下面)
这样即使点击“注销”,但是事实上并没有注销,即使退回到首页,但是我们还可以再次点击访问但是可以不通过账号、密码就可以访问刚才的页面。所以这样的做法是不科学的!所以前面的操作才是正确的
继续正确的操作(注销后再次访问刚才已经访问过的密码必须使用账号、密码),继续修改使
//开启注销功能,跳到到首页
http.logout().logoutSuccessUrl("/");
这样就可以做到即使注销也可以直接退出到首页,并且保证再次访问与刚才一样的页面也必须是使用账号、密码登陆才可以,保护了数据的安全!这也就是为什么不能使用上面的那种方式,直接在页面添加路由跳转到首页的原因!
(2)、权限控制(根据不同的用户显示不同的版块)
不仅需要security包还需要thymeleafSpringsecurity包进行整合!
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--thymeleaf和springsecurity整合包-->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
在index.html页面中添加xmlns:sec="http://www.thymeleaf.org/extras/spring-security"命名空间会出现themleaf和springSecurity的整合代码提示"
<!--登录注销-->
<div class="right menu">
<!--如果没有登录只显示登陆没有其他版块-->
<div sec:authorize="!isAuthenticated()">
<a class="item" th:href="@{/toLogin}">
<i class="address card icon"></i> 登录
</a>
</div>
<!--只有登陆才会有用户名-->
<div sec:authorize="isAuthenticated()">
<!--注销-->
<a class="item">
用户名: <span sec:authentication="name"></span>
<!--角色: <span sec:authentication="role"></span>-->
</a>
</div>
<!--只有登陆才会有注销-->
<div sec:authorize="isAuthenticated()">
<a class="item" th:href="@{/logout}">
<i class="sign-out icon"></i> 注销
</a>
</div>
</div
使用ip http://localhost:8001/登陆首页如下,只有“登陆”字样,没有“用户名和注销”
随便点击一个,使用对应拥有权限的的密码和账户登陆,我选择auth.inMemoryAuthentication() .withUser("ml").password("123456").roles("vip1","vip2")
,登陆如下
点击登陆之后可以看到有对应的“用户名和注销操作”
点击“注销操作”之后可以看到没有了对应权限操作的“用户名和注销操作”
但是以上还存在的问题是在没有任何相应权限的用户登陆的时候,应该在首页什么都不显示;而在登陆之后应该显示其具有权限操作的版块
<div>
<br>
<div class="ui three column stackable grid">
<div class="column" sec:authorize="hasRole('vip1')">
<div class="ui raised segment">
<div class="ui">
<div class="content">
<h5 class="content">Level 1</h5>
<hr>
<div><a th:href="@{/level1/1}"><i class="bullhorn icon"></i> Level-1-1</a></div>
<div><a th:href="@{/level1/2}"><i class="bullhorn icon"></i> Level-1-2</a></div>
<div><a th:href="@{/level1/3}"><i class="bullhorn icon"></i> Level-1-3</a></div>
</div>
</div>
</div>
</div>
<div class="column" sec:authorize="hasRole('vip2')">
<div class="ui raised segment">
<div class="ui">
<div class="content">
<h5 class="content">Level 2</h5>
<hr>
<div><a th:href="@{/level2/1}"><i class="bullhorn icon"></i> Level-2-1</a></div>
<div><a th:href="@{/level2/2}"><i class="bullhorn icon"></i> Level-2-2</a></div>
<div><a th:href="@{/level2/3}"><i class="bullhorn icon"></i> Level-2-3</a></div>
</div>
</div>
</div>
</div>
<div class="column" sec:authorize="hasRole('vip3')">
<div class="ui raised segment">
<div class="ui">
<div class="content">
<h5 class="content">Level 3</h5>
<hr>
<div><a th:href="@{/level3/1}"><i class="bullhorn icon"></i> Level-3-1</a></div>
<div><a th:href="@{/level3/2}"><i class="bullhorn icon"></i> Level-3-2</a></div>
<div><a th:href="@{/level3/3}"><i class="bullhorn icon"></i> Level-3-3</a></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="column" sec:authorize="hasRole('vip1')">
<div class="column" sec:authorize="hasRole('vip2')">
<div class="column" sec:authorize="hasRole('vip3')">
这样在没有任何相应权限的用户登陆的时候,应该在首页什么都不显示
但是使用账号、密码登陆ip http://localhost:8001/login之后可以看见显示具有相应权限的版块! .antMatchers("/").permitAll() .antMatchers("/level1/**").hasRole("vip1") .antMatchers("/level2/**").hasRole("vip2") .antMatchers("/level3/**").hasRole("vip3"); .withUser("ml").password("123456").roles("vip1","vip2") .and() .withUser("root").password("123").roles("vip1","vip2","vip3") .and() .withUser("guest").password("1").roles("vip1");
这样可以直接点击访问想要的操作!
同理使用 .withUser("root").password("123").roles("vip1","vip2","vip3")
登陆可以看见所有权限的版块
至此“权限控制操作”到此结束!
5、记住我以及首页定制
(1)、记住我
在配置类中的protected void configure(HttpSecurity http) throws Exception
方法中添加
//防止网站工具:get、post
http.csrf().disable(); //关闭csrf功能,登陆失败可能存在的原因
http.logout().logoutSuccessUrl("/");
//开启记住我的功能 cookie,默认保存两天
http.rememberMe();
使用 http.rememberMe();就开启了记住我功能
可以与之前的登陆页面比较一下“记住我”功能就有了!
然后关掉浏览器,再次使用iphttp://localhost:8001/访问,可以发现不用账号、密码可以直接访问之前访问过的页面,这就是记住我的功能的实现!
可以发现“记住我的功能”是通过cookie实现的,时间是两周
所以要是清除掉cookie,那么“记住我的功能”就会失效!
(2)、首页定制(登陆页面)因为之前演示的操作都是使用springSecurity中已经写好的登陆页面
获取角色:principal.authorities
要使用我们自己定义好的只需要在在配置类中的protected void configure(HttpSecurity http) throws Exception
方法中修改
//没有权限默认会到登陆页面,修改springSecurity到我们自己定义好的登陆页面,这样原来springSecurity中定义好的页面就不能使用,失效了
http.formLogin().loginPage("/toLogin");
所以使用我们自己定义好的登陆页面,没有了“记住我的功能”
但是登陆失败,可以看到路由,我们没有这个请求路径http://localhost:8001/usr/login
将login.html中的跳转路由修改如下(修改前
)
login.html中的跳转路由修改修改后
再次登陆测试
成功!
当然也可以这样修改!login.html中的登陆请求依然使用springSecurity规定的请求路径“/login”,而不是使用上面我们自己定义好的“toLogin”,并且在配置类中的方法 protected void configure(HttpSecurity http) throws Exception中添加修改如下
//没有权限默认会到登陆页面,修改springSecurity到我们自己定义好的登陆页面,这样原来springSecurity中定义好的页面就不能使用,失效了
http.formLogin().loginPage("/toLogin").loginProcessingUrl("/login");
这样也可以访问成功!
这样也会出现各种问题:因为在我们自己定义好的login.html中登陆请求的是 <input type="text" placeholder="Username" name="username"> 和 <input type="password" name="password">
,而按照 http.formLogin().loginPage("/toLogin").loginProcessingUrl("/login");虽然走的是“/toLogin”但是还是走的是springSecurity默认的路径参数规定。此时必须保证formLogin中的username、password和前端中的username、password相同,否则还是登陆不了
在配置类中做出如下修改:
//没有权限默认会到登陆页面,修改springSecurity到我们自己定义好的登陆页面,这样原来springSecurity中定义好的页面就不能使用,失效了
http.formLogin().usernameParameter("username").passwordParameter("password").loginPage("/toLogin").loginProcessingUrl("/login");
这样即使前端中的name属性中的值和人家底层默认的不一样,我们还是可以在这里修改与前端中的一样保证不出错!
所以两种方式定义首页
第一种
//没有权限默认会到登陆页面,修改springSecurity到我们自己定义好的登陆页面,这样原来springSecurity中定义好的页面就不能使用,失效了
http.formLogin().loginPage("/toLogin");
index.html页面跳转到登陆页面
login.html页面登陆
第二种
//没有权限默认会到登陆页面,修改springSecurity到我们自己定义好的登陆页面,这样原来springSecurity中定义好的页面就不能使用,失效了
http.formLogin().usernameParameter("username").passwordParameter("password").loginPage("/toLogin").loginProcessingUrl("/login");
当然在定制首页中再加一个“记住我的功能”
//开启记住我的功能 cookie
http.rememberMe().rememberMeParameter("remember");
首页定制成功!
6、上面用到的java配置类
package com.ml.springSecurity.config;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
//Aop横切的方式
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
//授权
//首页所有的人都可以访问,功能页只有对应有权限的人才可以访问
//请求授权的规则
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/level1/**").hasRole("vip1")
.antMatchers("/level2/**").hasRole("vip2")
.antMatchers("/level3/**").hasRole("vip3");
//没有权限默认会到登陆页面,修改springSecurity到我们自己定义好的登陆页面,这样原来springSecurity中定义好的页面就不能使用,失效了
http.formLogin().usernameParameter("username").passwordParameter("password").loginPage("/toLogin").loginProcessingUrl("/login");
//开启注销功能,跳到到首页
//防止网站工具:get、post
http.csrf().disable(); //关闭csrf功能,登陆失败可能存在的原因
http.logout().logoutSuccessUrl("/");
//开启记住我的功能 cookie
http.rememberMe().rememberMeParameter("remember");
}
//认证
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//这些数据正常是从数据库中读出的
//从内存中认证,也可以从数据库中查询相应角色的数据进行认证
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("ml").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2")
.and()
.withUser("root").password(new BCryptPasswordEncoder().encode("123")).roles("vip1","vip2","vip3")
.and()
.withUser("guest").password(new BCryptPasswordEncoder().encode("12")).roles("vip1");
}
}