springboot整合shiro
中国加油,武汉加油!
篇幅较长,配合右边目录观看
项目准备
-
创建springboot项目nz1904-springboot-07-shiro
-
导shiro相关依赖
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.3.2</version> </dependency>
-
application.properties配置模板引擎
# 配置模板引擎 spring.thymeleaf.prefix=classpath:/templates/ spring.thymeleaf.suffix=.html spring.thymeleaf.mode=HTML5 spring.thymeleaf.encoding=utf-8 spring.thymeleaf.cache=false
-
修改主启动类
1. 案例(不连接数据库测试)
1.1 定义一个Shiro的配置类ShiroConfig
package com.wpj.config;
import com.wpj.realm.MyRealm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Shiro整个配置文件
*/
@SpringBootConfiguration
public class ShiroConfig {
private Logger logger = LoggerFactory.getLogger(ShiroConfig.class);
// 配置过滤器,拦截请求
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
logger.info("shiroFilterFactoryBean。。。。。。。");
// 认证失败跳转页面
shiroFilterFactoryBean.setLoginUrl("/toLogin");
/**
* logout: 登出过滤器
* perms:权限控制
* roles: 具有某一角色才能访问
*/
Map<String, String> map = new LinkedHashMap<String, String>();
map.put("/login", "anon"); // anon匿名过滤器,不认证也可以访问
map.put("/**", "authc"); // authc认证过滤器:所有请求都必须在用户认证之后才能访问
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
return shiroFilterFactoryBean;
}
// 配置安全管理器
// securityManager必须要跟上面的过来securityManager名字一致
@Bean
public DefaultWebSecurityManager securityManager(@Qualifier("myRealm") MyRealm myRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
logger.info("securityManager。。。。。。。");
// 设置校验的Realm对象
securityManager.setRealm(myRealm);
return securityManager;
}
// 配置Realm
// 名字同上
@Bean
public MyRealm myRealm(){
MyRealm myRealm = new MyRealm();
logger.info("myRealm。。。。。。。");
return myRealm;
}
}
1.2 定义一个MyRealm类
package com.wpj.realm;
import com.wpj.pojo.User;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
/**
* shiro跟数据库交互的桥梁
*/
public class MyRealm extends AuthorizingRealm {
@Override
public String getName() {
return "MyRealm";
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 获取用户名
String userName = (String) authenticationToken.getPrincipal();
// 通过用户名查询用户对象
// 查询出来(模拟数据库)
if(!(userName.equals("jiekami"))){
return null;
}
User user = new User(1,"jiekami","123");
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user.getName(), user.getPwd(), getName());
return simpleAuthenticationInfo;
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
}
1.3 定义一个User对象,实现序列化
serialVersionUID 要自己生成
package com.wpj.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
private static final long serialVersionUID = 8434196542261610760L;
private Integer id;
private String name;
private String pwd;
}
1.4 新建主页和登录页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<base th:href="${#request.getContextPath()+'/'}">
</head>
<body>
this is index page!
</body>
</html>
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<base th:href="${#request.getContextPath()+'/'}">
</head>
<body>
<form action="/login" method="post">
姓名: <input type="text" name="name" /><span th:text="${nameError}"></span><br />
密码: <input type="text" name="pwd" /><span th:text="${pwdError}"></span><br />
<span th:text="${otherError}"></span>
<input type="submit" value="登录">
</form>
</body>
</html>
1.5 定义一个Controller
package com.wpj.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class UserController {
@RequestMapping("/toLogin")
public String toLogin(){
return "login";
}
@RequestMapping("/toIndex")
public String toIndex(){
return "index";
}
@RequestMapping("/login")
public String login(User user, Model model) {
// 封装成请求对象
UsernamePasswordToken token = new UsernamePasswordToken(user.getName(), user.getPwd());
// 获取登录的主题对象
Subject subject = SecurityUtils.getSubject();
try{
// 登录
subject.login(token);
} catch (UnknownAccountException e) {
logger.error("未知账户异常");
model.addAttribute("nameError","未知账户异常");
return "login";
} catch (IncorrectCredentialsException e) {
logger.error("密码错误");
model.addAttribute("pwdError","密码错误");
return "login";
} catch (Exception e) {
logger.error("其他问题登录失败" + e.fillInStackTrace());
model.addAttribute("otherError","其他问题登录失败" + e.fillInStackTrace());
return "login";
}
return "index";
}
}
1.6 启动主启动类
因为没连接数据库,springboot自动配置了url,如果不排除自动配置的类DataSourceAutoConfiguration会报错。
package com.wpj;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
1.7 测试
- localhost/toIndex 跳转到login页面,因为被拦截了。
- 输入jiekami和123 跳转到index页面
- 输入其他数据会回到login页面并且报错
-
项目结构图
2. 功能扩展
2.1 退出功能
2.1.1 index.html添加退出按钮
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<base th:href="${#request.getContextPath()+'/'}">
</head>
<body>
this is index page!
<a href="/logout">退出</a>
</body>
</html>
2.1.2 ShiroConfig配置 登出过滤器
map.put("/logout", "logout"); // logout登出过滤器
2.2 密码散列
2.2.1 ShiroConfig配置密码散列
// 密码散列
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher(){
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("MD5"); // MD5散列
hashedCredentialsMatcher.setHashIterations(1); // 散列次数
return hashedCredentialsMatcher;
}
2.2.2 修改MyRealm
User user = new User(1,"jiekami", "e99a18c428cb38d5f260853678922e03");
// 加盐
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
user.getName(), user.getPwd(), ByteSource.Util.bytes("abc"), getName());
2.3 显示用户信息
2.3.1 修改Realm
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
user, user.getPwd(), ByteSource.Util.bytes("abc"), getName());
2.3.2 修改Controller
// 获取第一个参数 --》 SimpleAuthenticationInfo(user, user.getPwd(), ByteSource.Util.bytes("abc"), getName());
User user1 = (User)SecurityUtils.getSubject().getPrincipal();
model.addAttribute("user", user1);
2.3.3. 修改index.Html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<base th:href="${#request.getContextPath()+'/'}">
</head>
<body>
欢迎<sapn th:text="${user.name}"></sapn>登录<br />
this is index page!
<a href="/logout">退出</a>
</body>
</html>
2.3.4 测试
版权声明:本文为TheNew_One原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。