javaweb的登录拦截请求,用拦截器也可以实现,但是得需要大量的代码。
使用springSecurity和shiro都可以实现同样的效果,下面来介绍一下shiro实现登录权限控制。
百度百科:
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
下面来做一些简单的例子,只是实现部分功能,其他的内容具体问题具体分析吧。
工具:idea2019
框架:springboot
前端引擎:thymeleaf
首先新建一个web项目导入maven依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--导入shiro依赖-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<!--mysql数据库的包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/druid-spring-boot-starter -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.1</version>
</dependency>
<!--为了实现shiro与thymeleaf的整合的包-->
<!-- https://mvnrepository.com/artifact/com.github.theborakompanioni/thymeleaf-extras-shiro -->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
</dependencies>
导入依赖之后开始进行配置
application.yml
这些是最基本的配置 关闭thymeleaf的缓存,配置mybatis,连接数据库,端口
spring:
thymeleaf:
cache: false
datasource:
username: root
password: 123
url: jdbc:mysql://localhost:3306/ssmbuild?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
mybatis:
type-aliases-package: com.pojo
mapper-locations: classpath:mapper/*.xml
server:
port: 8081
springboot的基本注解在这里就不说了。主要说一下shiro的配置。
三个核心组件:Subject, SecurityManager 和 Realms.
Subject就是当前操作的用户,
SecurityManager 是管理所有用户的安全操作,
Shiro会从应用配置的Realm中查找用户及其权限信息。
1.UserRealm是自定义的然后将其交给spring容器去管理
2.调用getDefaultWebSecurityManager这个方法来创建默认的权限管理者
3.调用getShiroFilterFactoryBean的构造函数来实例化一个过滤器
我们具体的权限设置也是在这里设置的。
public class UserRealm extends AuthorizingRealm {
@Autowired
UserService userService;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//System.out.println("doGetAuthorizationInfo");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermission("user:add");
Subject thisUser = SecurityUtils.getSubject();
//拿到User对象
User currentUser = (User)thisUser.getPrincipal();
//设置权限 从数据库中读取来设置
info.addStringPermission(currentUser.getPrems());
return info;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
User user = userService.searchByName(token.getUsername());
System.out.println(user);
if(user == null){
return null;
}
Subject subject = SecurityUtils.getSubject();
Session session = subject.getSession();
session.setAttribute("loginUser",user);
//密码认证
//传递user在授权中使用
return new SimpleAuthenticationInfo(user,user.getPassword(),"");
}
}
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager s){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(s);
//添加内置过滤器
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
//开始授权
filterChainDefinitionMap.put("/user/add","perms[user:add]"); //
filterChainDefinitionMap.put("/user/update","perms[user:update]"); //
filterChainDefinitionMap.put("/user/*","authc");
// filterChainDefinitionMap.put("/user/update","authc");
bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
//设置登录的请求
bean.setLoginUrl("/login");
bean.setUnauthorizedUrl("/noauth");
return bean;
}
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userR){
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(userR);
return defaultWebSecurityManager;
}
//自定义的认证和授权 需要自己配置
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
//整合ShiroDialect thymeleaf
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
}
下面写一个controller来进行测试
@Controller
public class MyController {
//因为
@RequestMapping({"/","/index"})
public String toIndex(Model model){
model.addAttribute("msg", "one");
return "/index2";
}
@RequestMapping("/user/add")
public String add(Model model){
return "/user/add";
}
@RequestMapping("/user/update")
public String update(Model model){
return "/user/update";
}
@RequestMapping("/login")
public String toLogin(String username,String password,Model model){
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try {
subject.login(token);
//登录成功返回首页
return "/index2";
} catch (UnknownAccountException e) {
model.addAttribute("msg","user is undefined");
return "/login";
} catch (IncorrectCredentialsException e){
model.addAttribute("msg","password is unkown");
return "/login";
}
}
@RequestMapping("/noauth")
@ResponseBody
public String unauthorized(){
//没有权限无法访问
return "unauthorized";
}
}
我们可以看到在controller中用SecurityUtils.getSubject()来获取了当前操作对象
然后调用login()方法来进行登录。具体的验证是由shiro去做的。如果是用拦截器来写的话
代码起码要翻一倍吧。(还可能有bug,如果让我写bug可能是改不完的,好尴尬)
下面贴上我在数据库设置的权限表
name和password就不说了。perms这个字段就是shiro用来拿这个去验证的
这里要注意这个权限按照常理来说是不可能为空的,这是我建表的失误。
拿这段代码为例
//第一个参数是路径,第二个参数是设置过滤权限
filterChainDefinitionMap.put("/user/add","perms[user:add]");
第一个参数是路径,第二个参数是设置过滤权限
也就是说访问add请求的时候你必须携带一个add权限否则无法访问。
最牛逼的地方啊,我觉得就是shiro和thymeleaf整合的地方。(我都惊呆了原来还可以这样)
看下html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
<head>
<meta charset="UTF-8">
<title>ppp</title>
</head>
<body>
<h1>首页</h1>
<p th:text="${msg}"></p>
<div th:if="${session.loginUser == null}">
<a th:href="@{/login}">登录</a>
</div>
<div shiro:hasPermission="user:add">
<a th:href="@{/user/add}">add</a>
</div>
<div shiro:hasPermission="user:update">
<a th:href="@{/user/update}">update</a>
</div>
</body>
</html>
如果 shiro:hasPermission="user:add"修饰在div上就可以直接控制该div根据权限是否显示。
这样就可以跑起来了,而这些代码只不过是可以小小的感觉一下shiro的作用。
如果想把登录写的更完善的话就需要罗列更多的代码。
了解来说的话,这样就差不多了,如果用到了可以再深究一下。
建议多看些数据结构和算法,这些插件和框架看看说明书找找资料就可以配置的,
但是,算法这个东西,不会就是不会啊!