准备工作:
md5加密算法:
public class md5Test {@Testpublic void md5() {//明文(123) + 盐值(monian) + 加密次数(1024) ==> 密文 8ea680082c12d7d878a3a97214ebbdc2Md5Hash 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: 8080servlet:context-path: /shiro
spring:mvc:view:prefix: /suffix: .jspdatasource:driver-class-name: com.mysql.cj.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSourcepassword: chenusername: rooturl: jdbc:mysql://localhost:3306/shiro?useSSL=false&userUnicode=true&characterEncoding=utf8druid:max-active: 10
mybatis-plus:configuration:map-underscore-to-camel-case: truelog-impl: org.apache.ibatis.logging.stdout.StdOutImpltype-aliases-package: net.wanho.entitymapper-locations: classpath:mapper/*.xml
创建Realm(数据源)对象:
/*** 自定义Realm ,继承AuthorizingRealm*/
public class ShiroRealm extends AuthorizingRealm {@Resourceprivate UserService userService;/*** 认证* Authentication 证明真实性,鉴定;身份验证*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {//先怼死 不要从数据库找数据 admin 123UsernamePasswordToken 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 批准书,授权书;批准*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {return null;}
}
添加shiro的配置:
@Configuration
public class ShiroConfig {/*** 获得认证的数据源* realm 领域、范围* @return*/@Beanpublic 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*/@Beanpublic DefaultWebSecurityManager getDefaultWebSecurityManager(Realm realm) {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(realm);return securityManager;}/*** ShiroFilter ,对资源进行过滤处理* 将SecurityManager交给ShiroFilterFactoryBean管理* @param securityManager* @return*/@Beanpublic 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*/@Beanpublic 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();//封装 tokenUsernamePasswordToken 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可以将数据带到前端界面