视频学习链接:
狂神说SpringBoot18:集成SpringSecurity
Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架。它实际上是保护基于spring的应用程序的标准。
需求:根据用户的权限,限制用户可进入的页面,如用户权限为VIP1可以访问/level1/下的所有页面,用户权限为VIP2可以访问/level2/下的所有页面,用户权限为VIP3可以访问/level3/下的所有页面。
<dependency>
<groupId>org.thymeleafgroupId>
<artifactId>thymeleaf-spring5artifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-securityartifactId>
dependency>
<dependency>
<groupId>org.thymeleaf.extrasgroupId>
<artifactId>thymeleaf-extras-springsecurity5artifactId>
dependency>
DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/html">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<form action="/index" method="post">
<p>账号:p><input type="text" placeholder="请输入账号" name="userName">
<p>注销:p><input type="password" placeholder="请输入密码" name="passWord">
br>
<input type="checkbox" name="remember">记住我
br>
<input type="submit" value="登入">
form>
body>
html>
index.html:网站主界面
DOCTYPE html>
<html lang="en" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<H1>首页H1>
<div class="right menu">
<div sec:authorize="!isAuthenticated()">
<a th:href="@{/tologin}" class="item"/>登入
div>
<div sec:authorize="isAuthenticated()">
<a class="item">
用户名: <span sec:authentication="name">span>
角色:<span sec:authentication="principal.authorities">span>
a>
div>
<div sec:authorize="isAuthenticated()">
<a th:href="@{/logout}" class="item"/>注销
div>
div>
<div sec:authorize="hasRole('VIP1')">
<h2>level1h2>
<a href="/level1/1">level1-1a>
<a href="/level1/2">level1-2a>
<a href="/level1/3">level1-3a>
div>
<div sec:authorize="hasRole('VIP2')">
<h2>level2h2>
<a href="/level2/1">level2-1a>
<a href="/level2/2">level2-2a>
<a href="/level2/3">level2-3a>
div>
<div sec:authorize="hasRole('VIP3')">
<h2>level3h2>
<a href="/level3/1">level3-1a>
<a href="/level3/2">level3-2a>
<a href="/level3/3">level3-3a>
div>
body>
html>
经过前面操作,基本上可以实现界面的跳转,但是还无法实现根据用户权限的不同,展示不同的页面,这个时候我们需要使用SpringSecurity。
@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");
// 开启登入
http.formLogin().loginPage("/tologin").usernameParameter("userName").passwordParameter("passWord").loginProcessingUrl("/index");
// 注销
http.logout().logoutSuccessUrl("/tologin");
http.csrf().disable();
// 开启记住我功能
http.rememberMe().rememberMeParameter("remember");
}
// 认证
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 从内存中定义
auth.inMemoryAuthentication()
.passwordEncoder(new BCryptPasswordEncoder())
.withUser("admin").password(new BCryptPasswordEncoder().encode("123456")).roles("VIP1")
.and()
.withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("VIP2")
.and()
.withUser("guest").password(new BCryptPasswordEncoder().encode("123456")).roles("VIP3");
}
}
WebSecurityConfigurerAdapter:自定义Security策略(自定义Spring Security 配置类继承该类即可)
@EnableWebSecurity:开启WebSecurity模式
AuthenticationManagerBuilder:自定义认证策略
Spring Security的两个主要目标是 “认证” 和 “授权”(访问控制):
重写configure(HttpSecurity http)方法用于授权
重写configure(AuthenticationManagerBuilder auth)方法用于认证
这个登入界面是使用自己编写的(SpringSecurity本身提供登入界面)
登入admin(权限为VIP1)
登入root(权限为VIP2)
登入guest用户(权限为VIP3)
Apache Shiro是一个功能强大且易于使用的Java安全框架,提供了认证,授权,加密,和会话管理。如同 Spring security 一样都是是一个权限安全框架,但是与Spring Security相比,在于他使用了和比较简洁易懂的认证和授权方式。
需求:通过查询用户的perms字段的值,如果为user:add表示该用户可以进入add.html;如果为user:update表示该用户进入update.html
<!--导入shiro依赖-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency>
<!--数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
<!DOCTYPE html>
<html lang="en"
xmlns:th="https://www.thymeleaf.org"
xmlns:shiro="https://shiro.apache.org/jsp-tag-library.html">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<p th:text="${msg}" style="color: red;"></p>
<shiro:notAuthenticated>
<a th:href="@{/toLogin}">登入</a>
</shiro:notAuthenticated>
<shiro:authenticated>
<shiro:haspermission name="user:add">
<a th:href="@{/user/add}">add</a>
</shiro:haspermission>
<shiro:haspermission name="user:update">
<a th:href="@{/user/update}">update</a>
</shiro:haspermission>
</shiro:authenticated>
</body>
</html>
login.html:用户登入界面
<!DOCTYPE html>
<html lang="en" xmlns:shiro="https://www.thymeleaf.org/thymeleaf-extras-shiro">
<head>
<meta charset="UTF-8">
<title>登入界面</title>
</head>
<body>
<form th:action="@{/submitLogin}" method="post">
<p th:text="${msg}" style="color: red;"></p>
账号:<input type="text" name="userName">
密码:<input type="password" name="passWord">
<input type="submit" value="登入">
</form>
</body>
</html>
User下的add.html和update.html随便写写即可:
@Controller
public class MyController {
@RequestMapping({"/","/index"})
public String toIndex(Model model){
model.addAttribute("msg","hello,shiro");
return "index";
}
@RequestMapping("/user/add")
public String add(){
return "/User/add";
}
@RequestMapping("/user/update")
public String update(){
return "/User/update";
}
@RequestMapping("/toLogin")
public String toLogin(){
return "login";
}
@RequestMapping("/submitLogin")
public String login(@RequestParam("userName") String username, @RequestParam("passWord")String passWord, Model model){
// 获取当前对象
Subject subject = SecurityUtils.getSubject();
// 封装用户的登入数据
UsernamePasswordToken token =new UsernamePasswordToken(username,passWord);
System.out.println("进行登入!");
try {
subject.login(token);// 执行登入方法
return "index";
}catch (UnknownAccountException e){
model.addAttribute("msg","用户名错误");
return "login";
}catch (IncorrectCredentialsException e){
model.addAttribute("msg","密码错误");
return "login";
}
}
@ResponseBody
@RequestMapping("/auth")
public String noauth(){
return "没有授权!";
}
}
数据表结构:
数据:
数据源配置application.yaml中:
spring:
datasource:
username: root
password: root
url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
driver-class-name: com.mysql.cj.jdbc.Driver
Mapper层:
不要忘了扫描一下包:
application.properties中
由于映射文件UserMapper.xml放在Java文件中(资源过滤),不然无法生成该UserMapper.xml:
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.xmlinclude>
includes>
resource>
UserService接口:
UserServiceImpl实现类:
三个bean对象:(userRealm,DefaultWebSecurityManager,ShiroFilterFactoryBean)
@Configuration
public class ShiroConfig {
// ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
// 设置安全管理器
bean.setSecurityManager(defaultWebSecurityManager);
// 添加shiro的内置过滤器
Map<String,String> filterMap =new LinkedHashMap<>();
filterMap.put("/user/add","perms[user:add]");
filterMap.put("/user/update","perms[user:update]");
filterMap.put("/user/*","authc");
bean.setFilterChainDefinitionMap(filterMap);
// 设置登入页面
bean.setLoginUrl("/toLogin");
bean.setUnauthorizedUrl("/auth");
return bean;
}
// DefaultWebSecurityManager
@Bean(name = "securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
// 关联UserRealm
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(userRealm);
return securityManager;
}
// 创建realm对象
@Bean
public UserRealm userRealm() {
return new UserRealm();
}
@Bean
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
}
public class UserRealm extends AuthorizingRealm {
@Autowired
private UserServiceImpl userService;
// 授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了=>授权doGetAuthorizationInfo");
// 进行授权
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 通过数据库授权
Subject subject = SecurityUtils.getSubject();
User user = (User) subject.getPrincipal();
info.addStringPermission(user.getPerms());
return info;
}
// 认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行了=>验证doGetAuthenticationInfo");
// // 获取用户名,密码
UsernamePasswordToken userToken = (UsernamePasswordToken) token;
User user = userService.getUserByName(userToken.getUsername());
if (user == null){
return null;
}
// 密码验证
return new SimpleAuthenticationInfo(user,user.getPwd(),"");
}
}
登入测试(只有update权限):