♥ ha ha ha I’m coming
Today we will talk about using Shiro to implement user login permissions
Introduction: Shiro
Apache Shiro is a powerful and easy-to-use Java security framework that performs authentication, authorization, password, and session management. Using Shiro’s easy-to-understand apis, you can quickly and easily obtain any application, from the smallest mobile applications to the largest web and enterprise applications.
There are three core components: Subject, SecurityManager, and Realms. Subject: current user. However, in Shiro, the concept of Subject does not just refer to people. It can also be a third-party process, a Daemon Account, or something similar. It simply means “what is currently interacting with the software.” Subject represents the security actions of the current user, and SecurityManager manages the security actions of all users. SecurityManager: It is the core of Shiro’s framework, a typical Facade pattern through which Shiro manages internal component instances and provides various services for security management. Realm: Realm acts as a “bridge” or “connector” between Shiro and application security data. That is, when authenticating a user (login) and authenticating a user (access control), Shiro looks up the user and their permission information from an application-configured Realm. In this sense, a Realm is essentially a security-related DAO: It encapsulates the connection details of the data source and provides related data to Shiro when needed. When configuring Shiro, you must specify at least one Realm for authentication and/or authorization. It is possible to configure multiple Realms, but at least one is required. Shiro has built-in Realms that can connect to a large number of secure data sources (aka directories), such as LDAP, relational databases (JDBC), ini-like text configuration resources, and properties files. If the default Realm does not meet your requirements, you can also insert your own Realm implementation that represents a custom data source.
Get straight to the point:
Step 1: Import our Pom file again
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional> </dependency> <! -- shiro --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4. 0</version>
</dependency>
<dependency>
<groupId>org.crazycake</groupId>
<artifactId>shiro-redis</artifactId>
<version>3.22.</version>
<exclusions>
<exclusion>
<groupId>com.puppycrawl.tools</groupId>
<artifactId>checkstyle</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0. 0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
Copy the code
Do you feel familiar, these are our common Pom files oh.
Step 2: The application.yml file is not much. Let’s go to the code:
Start the port
server:
port: 8888
spring:
application:
name: demo-shiro
Copy the code
This is too easy, using SpringBoot development is so good oh.
Step 4: Watch out, appetizers are coming…
Create User object :(User)
package com.demo.shiro.entity;
import lombok.Data;
import java.util.Set;
@Data
public class User {
/** * user Id */
private Long userId;
/** * User name */
private String userName;
/** * User password */
private String userPassword;
/** * User role */
private Set<String> role;
/** * User permission */
private Set<String> permission;
/** * constructor *@param userId
* @param userName
* @param userPassword
* @param role
* @param permission
*/
public User(Long userId, String userName, String userPassword, Set<String> role, Set<String> permission) {
this.userId = userId;
this.userName = userName;
this.userPassword = userPassword;
this.role = role;
this.permission = permission; }}Copy the code
Create data Response :(Response)
package com.demo.shiro.entity;
import java.util.HashMap;
import java.util.Map;
/** * Response data */
public class Response extends HashMap<String.Object> {
/** * info *@param message
* @return* /
public Response message(String message){
this.put("message", message);
return this;
}
/** * data *@param data
* @return* /
public Response data(Object data){
this.put("data", data);
return this;
}
/** * new method *@param key
* @param value
* @return* /
@Override
public Object put(String key, Object value) {
super.put(key, value);
return this; }}Copy the code
Create our custom SystemException
package com.demo.shiro.exception;
/** * The exception throws */
public class SystemException extends Exception{
public SystemException(String messgae){
super(messgae); }}Copy the code
Create a system utility class to simulate data information (SystemUtil) :
package com.demo.shiro;
import com.demo.shiro.entity.User;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;
import javax.lang.model.type.ArrayType;
import java.util.*;
/** * simulate database data */
public class SystemUtils {
/** * Simulate two data ** in the database@return* /
public static List<User> users(a) {
List<User> users = new ArrayList<>();
// Add data
users.add(new User(
1L."admin"."123456".new HashSet<>(Collections.singleton("admin")),
new HashSet<>(Arrays.asList("user:add"."user:delete"))));
users.add(new User(
2L."register"."123456".new HashSet<>(Collections.singleton("register")),
new HashSet<>(Arrays.asList("user:view"))));
return users;
}
/** * Get user *@param username
* @return* /
public static User getUser(String username){
List<User> users = SystemUtils.users();
return users.stream().filter(user ->
StringUtils.equalsAnyIgnoreCase(username,user.getUserName())).findFirst().orElse(null); }}Copy the code
OK, at this point, we have created all the basic classes.
Step 5: Create our Shiro-related configuration information
Shiro configuration class :(ShiroConfig)
package com.demo.shiro.config;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.SessionListener;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.*;
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
/ / set the securityManager
shiroFilterFactoryBean.setSecurityManager(securityManager);
return shiroFilterFactoryBean;
}
@Bean
public SecurityManager securityManager(ShiroRealm shiroRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// Configure the SecurityManager and inject shiroRealm
securityManager.setRealm(shiroRealm);
return securityManager;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
@Bean(name = "lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(a) {
return new LifecycleBeanPostProcessor();
}
/** * Resolve the issue of annotations not working *@return* /
@Bean
public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator(a){
return newDefaultAdvisorAutoProxyCreator(); }}Copy the code
Shiro Authentication and Authorization Classes (ShiroRelam)
package com.demo.shiro.config;
import com.demo.shiro.SystemUtils;
import com.demo.shiro.entity.User;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.stereotype.Component;
@Component
public class ShiroRealm extends AuthorizingRealm {
/** * Login module *@param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// Get the user name
String username = (String) authenticationToken.getPrincipal();
// Get the password
String password = new String((char[]) authenticationToken.getCredentials());
// Get the user based on the user name
User user = SystemUtils.getUser(username);
if (user == null| |! StringUtils.equals(password, user.getUserPassword())){throw new IncorrectCredentialsException("Wrong username or password");
}
// Successful login
return new SimpleAuthenticationInfo(user, user.getUserPassword(), getName());
}
/** * Authorization module *@param principalCollection
* @return* /
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
User user = (User) SecurityUtils.getSubject().getPrincipal();
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
// Set the role to simulate the database
simpleAuthorizationInfo.setRoles(user.getRole());
// Set permissions
simpleAuthorizationInfo.setStringPermissions(user.getPermission());
returnsimpleAuthorizationInfo; }}Copy the code
Does it feel easy? Yes, just configure these two classes to complete Shiro…
Step 6: Now we can start our test… Are you ready?
Create a landing page :(login.html)
<! DOCTYPEhtml>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>In the test</title>
</head>
<body>
<form action="/login" method="post">
<input type="text" name="username">
<br>
<input type="password" name="password">
<br>
<input type="submit" value="Login">
</form>
</body>
</html>
Copy the code
Create a ViewController to access our page:
package com.demo.shiro.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/** * page control **/
@Controller
public class ViewController {
@RequestMapping("login.html")
public String showLogin(a){
return "login"; }}Copy the code
This page does not need to show, this is too simple…
Create our LoginController (LoginController)
package com.demo.shiro.controller;
import com.demo.shiro.entity.Response;
import com.demo.shiro.exception.SystemException;
import com.demo.shiro.service.LoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
/** * log in to Controller */
@Controller
public class LoginController {
@Autowired(required = false)
private LoginService loginService;
/** * login *@param username
* @param password
* @return* /
@PostMapping("login")
public ResponseEntity<Response> login(@RequestParam("username") String username,
@RequestParam("password") String password) throws SystemException {
Response response = this.loginService.login(username, password);
returnResponseEntity.ok(response); }}Copy the code
There may be an error, don’t panic, because our LoginService was not created…
Create our LoginService (LoginService) :
package com.demo.shiro.service;
import com.demo.shiro.entity.Response;
import com.demo.shiro.exception.SystemException;
public interface LoginService {
/ / login
Response login(String username, String password) throws SystemException;
}
Copy the code
Create its implementation class (LoginServiceImpl)
package com.demo.shiro.service.impl;
import com.demo.shiro.SystemUtils;
import com.demo.shiro.entity.Response;
import com.demo.shiro.entity.User;
import com.demo.shiro.exception.SystemException;
import com.demo.shiro.service.LoginService;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Service;
/** * log in to Service */
@Service
public class LoginServiceImpl implements LoginService {
/** * login operation *@param username
* @param password
* @return* /
@Override
public Response login(String username, String password) throws SystemException {
Subject subject = SecurityUtils.getSubject();
// Check whether it exists
if (StringUtils.isBlank(username) || StringUtils.isBlank(password)){
throw new SystemException("Please enter information");
}
try {
// Perform login
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);
subject.login(usernamePasswordToken);
User user = (User) subject.getPrincipal();
return new Response().message("Certification successful").data(user);
} catch (AuthenticationException e) {
e.printStackTrace();
throw new SystemException("Authentication failed"); }}}Copy the code
The code in here is also easy to understand, the code quality is poor… Hahaha, need to optimize their own oh…
At this point, our log-in logic ends… Does it feel easy…
It’s time for our test. Are you ready?
Step 7: Test environment:
Create TestController (TestController)
package com.demo.shiro.controller;
import com.demo.shiro.entity.Response;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.annotation.security.RolesAllowed;
/** * Test Controller */
@Controller
@RequestMapping("test")
public class TestController {
/** * You need the admin role to access *@return* /
@GetMapping("admin")
@RequiresRoles("admin")
public ResponseEntity<Response> test1(a){
return ResponseEntity.ok(new Response().message("You are the admin"));
}
/** * Have permission to view *@return* /
@GetMapping("view")
@RequiresPermissions("admin:view")
public ResponseEntity<Response> test2(a){
return ResponseEntity.ok(new Response().message("You have access.")); }}Copy the code
There are two annotations that I have not met before, so you will learn this article oh…
Here is only the implementation of login, no filters, interceptors set… We need to log in to access the page… Have you ever thought about it… Ha, ha, ha
Directory screenshot:
Test screenshot:
Login interface:
Test login with admin: After successful login, we will see the following message
I believe you understand, yes is our own set up simulation data oh. Next we’ll look at other features… Access the test/admin interface:Test /view = test/viewDo you see this page is very panic, do not panic, we want to steady, a careful look, the original we do not have this permission, so… Understand!
OK, here we are, we have a demo, we have a registered user, we can test ourselves… GitHub address: shiro-demo good, write so long finally finished ,,,, very tired oh… If you have any questions please feel free to comment in the comments at ,,,, thank you for browsing… mua