准备工作:
md5加密算法:
public class md5Test {
@Test
public void md5() {
//明文(123) + 盐值(monian) + 加密次数(1024) ==> 密文 8ea680082c12d7d878a3a97214ebbdc2
Md5Hash md5Hash = new Md5Hash("123","monian",1024);
System.out.println(md5Hash);
}
}
创建Springboot项目web、Lombok:
由于我们使用的是jsp,需要引入相关的依赖(防止访问页面时,访问的是资源路径(访问后不解析,会直接下载文件))
此外还有shiro依赖、mybatis-plus、mysql、druid:
<!--jsp-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<!--shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.5.3</version>
</dependency>
<!-- mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.6</version>
</dependency>
properties.yml配置文件:
server:
port: 8080
servlet:
context-path: /shiro
spring:
mvc:
view:
prefix: /
suffix: .jsp
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
password: chen
username: root
url: jdbc:mysql://localhost:3306/shiro?useSSL=false&userUnicode=true&characterEncoding=utf8
druid:
max-active: 10
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
type-aliases-package: net.wanho.entity
mapper-locations: classpath:mapper/*.xml
创建Realm(数据源)对象:
/**
* 自定义Realm ,继承AuthorizingRealm
*/
public class ShiroRealm extends AuthorizingRealm {
@Resource
private UserService userService;
/**
* 认证
* Authentication 证明真实性,鉴定;身份验证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//先怼死 不要从数据库找数据 admin 123
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;
//用户输入用户名
String username = usernamePasswordToken.getUsername();
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("username",username);
User user = userService.getOne(wrapper);
//账号不存在
if(ObjectUtils.isEmpty(user)){
throw new UnknownAccountException("账号不存在!!!");
}
// String password = (String) usernamePasswordToken.getCredentials();
// if (!password.equals("8ea680082c12d7d878a3a97214ebbdc2")){
// throw new IncorrectCredentialsException("账号密码错误!!!");
// }
//密文密码
//存放盐值
ByteSource byteSource = ByteSource.Util.bytes(user.getSalt());
return new SimpleAuthenticationInfo(username,user.getPassword(),byteSource,super.getName());
}
/**
* 授权
* Authorization 批准书,授权书;批准
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
}
添加shiro的配置:
@Configuration
public class ShiroConfig {
/**
* 获得认证的数据源
* realm 领域、范围
* @return
*/
@Bean
public Realm getRealm() {
ShiroRealm realm = new ShiroRealm();
//使用 md5 加密
//创建密码匹配器,支持散列算法
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
credentialsMatcher.setHashAlgorithmName("MD5"); //设置加密算法
credentialsMatcher.setHashIterations(1024); //设置散列次数
//设置密码匹配器
realm.setCredentialsMatcher(credentialsMatcher);
return realm;
}
/**
* 创建安全管理器 类似于: 自定义XXService
* 自定义的Realm交给SecurityManager管理
* @param realm
* @return
*/
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(Realm realm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(realm);
return securityManager;
}
/**
* ShiroFilter ,对资源进行过滤处理
* 将SecurityManager交给ShiroFilterFactoryBean管理
* @param securityManager
* @return
*/
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
//设置过滤路径 anon 默认 authc 认证 roles perms 授权 logout 退出
Map<String,String> map = new LinkedHashMap<>();
map.put("/index.jsp","authc");
map.put("/login.jsp","anon");
//设置安全管理器
filterFactoryBean.setSecurityManager(securityManager);
filterFactoryBean.setFilterChainDefinitionMap(map);
filterFactoryBean.setLoginUrl("/login.jsp");
return filterFactoryBean;
}
}
配置mybatis-plus的配置:
/**
* 配置类: 类似于 spring => *.xml
*/
@Configuration
@MapperScan("net.wanho.mapper")
public class MybatisPlusConfig {
/**
* 配置拦截器
* @return
*/
@Bean
public MybatisPlusInterceptor configMybatisPlusInterceptor(){
//创建拦截器实例
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//添加 分页拦截器 指定数据库
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
添加实体:
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("t_user")
public class User implements Serializable {
private Integer id;
private String username;
private String password;
private String salt;
}
mapper:
public interface UserMapper extends BaseMapper<User> {
User selectUsernameAndPassword(String username);
}
service:
public interface UserService extends IService<User> {
}
@Service
@Transactional(rollbackFor = Exception.class)
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
controller:
@Controller
@RequestMapping("/user")
public class UserController {
@PostMapping("/login")
public String login(String username, String password, Model model){
try {
//获得用户主体
Subject subject = SecurityUtils.getSubject();
//封装 token
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
//调用登录
subject.login(token);
//subject.checkPermission("user:view");
//跳转到主页
return "index";
} catch (Exception e) {
//设置错误消息
model.addAttribute("msg","账号或密码错误!!!") ;
//跳到到登录页面
return "login";
}
}
@GetMapping("/logout")
public String logout(){
Subject subject = SecurityUtils.getSubject();
subject.logout();
return "redirect:/login.jsp";
}
}
注意注解!!!,不能使用@RestController,返回的是json/xml文件,不会进行解析;
前端界面:
index.jsp:
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<%@page contentType="text/html;UTF-8" pageEncoding="UTF-8" isErrorPage="false" %>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<%--受限资源--%>
<h1>系统主页</h1>
<h2>欢迎: <shiro:principal/> </h2>
<a href="${pageContext.request.contextPath}/user/logout">安全退出</a>
<h1>系统主页</h1>
<ul>
<li><a href="#">用户管理</a></li>
<li><a href="#">商品管理</a></li>
<li><a href="#">订单管理</a></li>
<li><a href="#">物流管理</a></li>
</ul>
</body>
</html>
login.jsp:
<%@page contentType="text/html;UTF-8" pageEncoding="UTF-8" isErrorPage="false" isELIgnored="false" %>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<style>
.massage{
color: red;
}
</style>
<body>
<h1>登录界面</h1>
<a href="${pageContext.request.contextPath}/user/logout">安全退出</a>
<form action="${pageContext.request.contextPath}/user/login" method="post">
用户名:<input type="text" name="username" > <br/>
密码:<input type="password" name="password"> <br>
<input type="submit" value="登录">
</form>
<hr>
<h4 class="massage">${msg}</h4>
</body>
</html>
浏览器测试:
当点击安全退出按钮,默认返回的是login.jsp界面,再次输入index.jsp,也会跳到登陆界面
知识点梳理:
Spring MVC中内嵌9大成员,使用model可以将数据带到前端界面