二、Shiro安全框架-登陆实现
写在前面
上一章我们已经了解了Shiro的基本信息和架构,接下来我们用Shiro实现一个简单的登录。
1.登陆实现
1.1.搭建项目
创建一个名为”shiro-demo“的基于SpringBoot的web工程。
1.2.导入依赖
继承SpringBoot的父工程,导入web依赖和shiro整合spring的依赖以及其他相关依赖。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
<version>4.12</version>
</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>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.6</version>
</dependency>
</dependencies>
1.3.主启动类
@SpringBootApplication
@MapperScan("cn.demo.shiro.mapper")
public class StartApplication {
public static void main(String[] args) {
SpringApplication.run(StartApplication.class);
}
}
1.4.application.yml配置
配置端口和数据库相关
server:
port: 8000
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql:///shiro
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
mybatis:
mapper-locations: classpath:cn/demo/shiro/mapper/*Mapper.xml
1.5.web控制器
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/login")
public String login(String username, String password){
Subject subject = SecurityUtils.getSubject();
try {
if (!subject.isAuthenticated()){
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
subject.login(token);
}
return "用户名:"+username+"登陆成功!";
} catch (UnknownAccountException uae ) {
return "用户名错误!";
} catch (IncorrectCredentialsException ice ) {
return "密码错误!";
} catch (LockedAccountException lae ) {
return "账号已被锁定!";
} catch (AuthenticationException ae ) {
return "身份认证失败!";
}
}
@RequestMapping("/list")
public String getList(){
return "/user/list";
}
@RequestMapping("/add")
public String addUser(){
return "/user/add";
}
@RequestMapping("/unlogin")
public String unlogin(){
return "{'code':9999;msg:'未登录!'}";
}
}
1.6.自定义Realm
自定义的Realm我们一般通过继承AuthorizingRealm类来做自己具体的实现。
public class MyRealm extends AuthorizingRealm {
@Resource
private UserMapper userMapper;
@Override
public String getName() {
return "myRealm";
}
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof UsernamePasswordToken;
}
@Override
public AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String username = (String)token.getPrincipal();
User user = userMapper.selectByUsername(username);
if(Objects.isNull(user)) {
throw new UnknownAccountException("用户名错误,请重新输入用户名!");
}
return new SimpleAuthenticationInfo(user, user.getPassword(), getName());
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
}
1.7.Shiro配置
@Configuration
public class ShiroConfig {
@Bean
public MyRealm getMyRealm(){
MyRealm myRealm = new MyRealm();
myRealm.setCredentialsMatcher(new HashedCredentialsMatcher(Sha256Hash.ALGORITHM_NAME));
return myRealm;
}
@Bean
public SecurityManager securityManager(Realm realm){
return new DefaultWebSecurityManager(realm);
}
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
HashMap<String, String> filterChainDefinitionMap = new HashMap<>();
filterChainDefinitionMap.put("/user/login", "anon");
filterChainDefinitionMap.put("/user/list", "anon");
filterChainDefinitionMap.put("/**", "authc");
shiroFilterFactoryBean.setSecurityManager(securityManager);
shiroFilterFactoryBean.setLoginUrl("/user/unlogin");
shiroFilterFactoryBean.setSuccessUrl("/user/index");
shiroFilterFactoryBean.setUnauthorizedUrl("/user/unAuthorized");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher(){
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName(Sha256Hash.ALGORITHM_NAME);
return hashedCredentialsMatcher;
}
}
2.模拟登录,访问资源
打开浏览器,在地址栏输入localhost:8000/user/list 在未登录的情况下成功访问到资源,因为我们在shiro的过滤配置中对这个资源设置的无需认证就可以访问
在地址栏输入localhost:8000/user/add,点击回车后显示未登录信息 因为我们在shiro的过滤配置中没有对/user/add这个资源进行单独配置访问权限,那么这个资源会被所配置的通配符配置进行拦截到,所以该资源就需要认证之后才能进行访问
在地址栏输入localhost:8000/user/login?username=root&password=root,点击回车后进行登录认证 这个时候我们可以看到已经显示登陆成功,这个时候再访问/user/add则可以成功访问 到这里我们的简单认证登录实现就已经完成了
3.认证流程小结
SpringBoot整合Shrio的登录认证案例到这里就结束了,这篇文章没有对原理做过多的介绍,只是带大家进行一个shiro认证实现过程,这个案例的认证流程如下:
-
用户在输入用户名密码提交登陆后会由Shiro来为我们进行认证,我们将用户输入的用户名和密码封装成Shiro认证所需要的UsernamePasswordToken对象,然后调用Sebject的login方法 -
Shrio使用DefualtSecurityManager调用ModularRealmAuthenticator领域认证器选择数据源 -
我们自定义的Realm为登录认证提供安全数据(用户名、密码),通过用户名到数据库进行匹配 -
使用凭证匹配器(通过我们配置的密码编码器)来进行密码匹配,如果匹配成功就跳转到登录页面,认证失败就返回错误信息
4.总结
本篇文章到这里就结束了,如果觉得对屏幕前的你有帮助的话就请来个一键三连吧,欢迎大家学习讨论,如果有不足之处请尽情指出,大家一起进步,学无止尽,Never Give Up!!!
|