This is the fourth day of my participation in the August Challenge. For details, see:August is more challenging
1. An overview of the
1.1 SpringBoot
This is not much to say, can see this tutorial, estimated all can say to be proficient in the use of SpringBoot
1.2 Shiro
A security framework, but not just a security framework. It can achieve a variety of functions. It’s not just limited to the Web tier. In the domestic market share is higher than SpringSecurity, is the most used security framework
Users can be authenticated and authorized. Much simpler than SpringSecurity.
1.3 Jwt
It is a technique that can be used to authenticate between client and server, replacing the insecure use of Session authentication
Why not Session?
After the login, the client and server each store a corresponding SessionId. Each time the client initiates a request, the client carries this SessionId for comparison
- Sessions are too expensive for the server when the number of user requests is high
- Session is not a good way to cluster servers (i.e., you have to access the original server to get the corresponding Session ID)
It uses a token technique
The Jwt string is divided into three parts
-
Header
Store two variables
- Secret key (can be used for comparison)
- Algorithm (which encrypts the Header and payload as Signature)
-
payload
Store a lot of things, the basic information is as follows
- The signer, the user to which the token belongs. The general is
userId
- Creation time, when was this token created
- Expiration time, when does this token expire
- A unique identifier, which can generally be generated using an algorithm
- The signer, the user to which the token belongs. The general is
-
Signature
This is generated by the above two encryption algorithms in the Header. It is used to compare information and prevent tampering with the Header and payload
Then the information of these three parts is encrypted to generate a string of JwtToken and sent to the client. The client saves it locally. When the client initiates the request, it carries it to the server (it can be in the cookie, it can be in the header, it can be in the localStorage) and authenticates it on the server
Well, no more nonsense, the next start of actual combat, actual combat is divided into the following parts
SpringBoot
integrationShiro
SpringBoot
integrationJwt
SpringBoot
+Shiro
+Jwt
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.11.0</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
Copy the code
2. SpringBoot integrates Shiro
Two ways:
- Write the SSM integration configuration in springBoot using Java code
- Use the official start
2.1 Integrating springBoot with Start
pom.xml
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-web-starter</artifactId> The < version > 1.4.0 < / version > < / dependency > <! -- shiro-spring-boot-starter-->Copy the code
application.properties
Shiro. UnauthorizedUrl=" XXX "# Redirect page that does not pass the authorizationCopy the code
Create shiroconfig.java for some simple configuration
@Configuration public class SpringShiroConfig { @Bean public Realm customRealm() { return new CustomRealm(); } @Bean public DefaultWebSecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(customRealm()); // Disable the ShiroDAO function DefaultSubjectDAO subjectDAO = New DefaultSubjectDAO(); DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator(); / / no need to Shiro things to any places in the Session (including Http Session) defaultSessionStorageEvaluator. SetSessionStorageEnabled (false); subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator); securityManager.setSubjectDAO(subjectDAO); return securityManager; } @Bean public ShiroFilterChainDefinition shiroFilterChainDefinition() { DefaultShiroFilterChainDefinition chain = new DefaultShiroFilterChainDefinition(); AddPathDefinition ("/login", "anon"); // Which requests can be accessed anonymously by chain.addPathDefinition("/login", "anon"); AddPathDefinition ("/notLogin", "anon"); AddPathDefinition ("/403", "anon"); // Interface chain.addPathDefinition("/403", "anon"); AddPathDefinition ("/**", "authc"); addPathDefinition("/**", "authc"); return chain; } / / Shiro and Spring AOP integration of special setup @ Bean public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator () { DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator(); creator.setProxyTargetClass(true); return creator; } // Turn off the ShiroDao functionCopy the code
Create a custom Realm
public class CustomRealm extends AuthorizingRealm { private static final Set<String> tomRoleNameSet = new HashSet<>(); private static final Set<String> tomPermissionNameSet = new HashSet<>(); private static final Set<String> jerryRoleNameSet = new HashSet<>(); private static final Set<String> jerryPermissionNameSet = new HashSet<>(); static { tomRoleNameSet.add("admin"); jerryRoleNameSet.add("user"); tomPermissionNameSet.add("user:insert"); tomPermissionNameSet.add("user:update"); tomPermissionNameSet.add("user:delete"); tomPermissionNameSet.add("user:query"); jerryPermissionNameSet.add("user:query"); } @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { String username = (String) principals.getPrimaryPrincipal(); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); if (username.equals("tom")) { info.addRoles(tomRoleNameSet); info.addStringPermissions(tomPermissionNameSet); } else if (username.equals("jerry")) { info.addRoles(jerryRoleNameSet); info.addStringPermissions(jerryPermissionNameSet); } return info; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { String username = (String) token.getPrincipal(); If (username == null) throw new UnknownAccountException(" username cannot be null "); SimpleAuthenticationInfo info = null; if (username.equals("tom")) return new SimpleAuthenticationInfo("tom", "123", CustomRealm.class.getName()); else if (username.equals("jerry")) return new SimpleAuthenticationInfo("jerry", "123", CustomRealm.class.getName()); else return null; }}Copy the code
2.2 Not using starter
<! <dependency> <groupId>org.apache.shiro</groupId> < artifactId > shiro - spring < / artifactId > < version > 1.4.1 < / version > < / dependency >Copy the code
Write Shiro’s configuration class: ShiroConfig
To rewrite Shiro’s configuration information (spring-shiro.xml and spring-web.xml) in Java code configuration:
@Configuration public class ShiroConfig { @Bean public Realm realm() { return new CustomRealm(); } @Bean public DefaultWebSecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(realm()); return securityManager; } @Bean public ShiroFilterFactoryBean shirFilter() { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager()); shiroFilterFactoryBean.setLoginUrl("/loginPage"); shiroFilterFactoryBean.setUnauthorizedUrl("/403"); Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); filterChainDefinitionMap.put("/loginPage", "anon"); filterChainDefinitionMap.put("/403", "anon"); filterChainDefinitionMap.put("/login", "anon"); filterChainDefinitionMap.put("/hello", "anon"); filterChainDefinitionMap.put("/**", "authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } /* ################################################################# */ @Bean public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } @Bean @DependsOn("lifecycleBeanPostProcessor") public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); / / forced to specify annotations of the underlying implementation using defaultAdvisorAutoProxyCreator. Additional protocol setProxyTargetClass (true); return defaultAdvisorAutoProxyCreator; } @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) { AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); advisor.setSecurityManager(securityManager); return advisor; }}Copy the code
Writing the Controller
Same with Shiro and SSM integration. slightly
Write the Thymeleaf page
slightly
3. SpringBoot integrates Jwt
3.1 depend on
Springboot 2. Java-jwt -- core dependency 3. JJWT -- auxiliary help module for Java versionCopy the code
3.2 code
-
Create JwtUtil
package cn.coderymy.utils; import java.util.*; import com.auth0.jwt.*; import com.auth0.jwt.algorithms.Algorithm; import io.jsonwebtoken.*; import org.apache.commons.codec.binary.Base64; import java.util.*; public class JwtUtil { // The generated signature is the secret key used private final String base64EncodedSecretKey; // The encryption algorithm used to generate the signature private final SignatureAlgorithm signatureAlgorithm; public JwtUtil(String secretKey, SignatureAlgorithm signatureAlgorithm) { this.base64EncodedSecretKey = Base64.encodeBase64String(secretKey.getBytes()); this.signatureAlgorithm = signatureAlgorithm; } /** * Generate JWT Token string **@paramIss Name of issuer *@paramTtlMillis JWT expiration time *@paramClaims additional information added to the Netherlands section. * For example, you can add information such as user name, user ID, user (before encryption) password */ public String encode(String iss, long ttlMillis, Map<String, Object> claims) { if (claims == null) { claims = new HashMap<>(); } // Issue time (IAT) : one of the standard fields of the load section long nowMillis = System.currentTimeMillis(); Date now = new Date(nowMillis); // You can add standard and private declarations for the payload JwtBuilder builder = Jwts.builder() // Non-standard fields/additional fields in the load section, usually written before the standard fields. .setClaims(claims) // JWT ID (JTI) : one of the standard fields of the load part, the unique identifier of JWT. Although it is not required, try to ensure its uniqueness. .setId(UUID.randomUUID().toString()) // Issue time (IAT) : one of the standard fields in the load section, representing the generation time of this JWT. .setIssuedAt(now) // Signer (ISS) : one of the standard fields of the load section, representing the owner of this JWT. Usually, the content is representative of the user, such as username and userID. .setSubject(iss) // Set the algorithm and key for generating the signature .signWith(signatureAlgorithm, base64EncodedSecretKey); if (ttlMillis >= 0) { long expMillis = nowMillis + ttlMillis; Date exp = new Date(expMillis); // Expiration time (EXP) : one of the standard fields in the load section, representing the validity of this JWT. builder.setExpiration(exp); } return builder.compact(); } /** * JWT Token consists of three parts: head load part and signature part. The signature part is generated by the encryption algorithm and cannot be decrypted reversely. * The header and load sections are generated by the Base64 encoding algorithm and can be reverse coded back. * This is why you should not put sensitive data in JWT tokens. * *@paramJwtToken Encrypted token *@returnClaims returns key value pairs */ for the load part public Claims decode(String jwtToken) { / / get DefaultJwtParser return Jwts.parser() // Set the key for the signature .setSigningKey(base64EncodedSecretKey) // Set the JWT to parse .parseClaimsJws(jwtToken) .getBody(); } /** * Verify token * You can use the official verification or custom verification rules. For example, the token carries a password, which is encrypted and compared with the encrypted password in the database. * *@paramJwtToken Indicates the JWT Token */ to be verified public boolean isVerify(String jwtToken) { Algorithm algorithm = null; switch (signatureAlgorithm) { case HS256: algorithm = Algorithm.HMAC256(Base64.decodeBase64(base64EncodedSecretKey)); break; default: throw new RuntimeException("This algorithm is not supported"); } JWTVerifier verifier = JWT.require(algorithm).build(); verifier.verify(jwtToken); // If the check fails, an exception is thrown /* // Get DefaultJwtParser Claims Claims = decode(jwtToken); if (claims.get("password").equals(user.get("password"))) { return true; } * / return true; } public static void main(String[] args) { JwtUtil util = new JwtUtil("tom", SignatureAlgorithm.HS256); Map<String, Object> map = new HashMap<>(); map.put("username"."tom"); map.put("password"."123456"); map.put("age".20); String jwtToken = util.encode("tom".30000, map); System.out.println(jwtToken); /* util.isVerify(jwtToken); System. The out. Println (" legal "); * / util.decode(jwtToken).entrySet().forEach((entry) -> { System.out.println(entry.getKey() + ":"+ entry.getValue()); }); }}Copy the code
Resolution:
- Several values need to be passed in when creating a JwtUtil object
- This user is used to generate the secret key
- This encryption algorithm is used to generate JWT encryption
- How to get user information from JWT data (decode())
- A method to determine whether JWT exists or is out of date
- Finally, the test method
- Several values need to be passed in when creating a JwtUtil object
-
Create a Controller
- Login Controller
- Obtain the username and password and verify them with the database. If the verification succeeds, go to the next step
- You create a JwtUtil object, passing in username and the encryption algorithm you want to use
- Creates a map object that needs to add some basic information to the payload
- Create a MAP of the JWT data, passing in the username, save time, and basic information
- Check the Controller
- Gets the Jwt data passed in the foreground
- use
JWTUtil
In theisVerify
Verify that the JWT data is valid
- Login Controller
4. SpringBoot+Shiro+Jwt
-
Because you need to set Shiro’s SecurityManager, you cannot use Shro-spring-boot-starter to integrate with SpringBoot. Only Spring-Shiro can be used
<! -- Automatically import shro-core and Shro-web --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.1</version> </dependency> Copy the code
-
Because of the need to implement a stateless Web, Shiro’s Session feature is not available, but it should be turned off
public class JwtDefaultSubjectFactory extends DefaultWebSubjectFactory { @Override public Subject createSubject(SubjectContext context) { // Do not create session context.setSessionCreationEnabled(false); return super.createSubject(context); }}Copy the code
This will throw an exception if the getSession() method is called
4.1 process
- The user requests, without the token, to throw an exception/return no login in the JwtFilter and let it log in
- User request, carry token, get JWT from JwtFilter, encapsulated as JwtToken object. JwtRealm is then used for authentication
- Authentication is performed in JwtRealm to determine whether the token is valid, i.e
Execution process: 1. The client initiates a request, shiro filters take effect, and checks whether the request is a Login or logout request<br/>If yes, execute the request directly<br/>If not, enter JwtFilter2. JwtFilter execution process 1. If no, throw exception 2. If no, throw exception 2. The obtained JWT string is encapsulated in the created JwtToken, and the verification is performed using the login() method using Subject. This method calls the created JwtRealm 3. Execute the authentication method in JwtRealm, using`jwtUtil.isVerify(jwt)`4. Return true to keep the base runningCopy the code
4.2 Quick Start
0. JwtDeafultSubjectFactory
package cn.coderymy.shiro;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.subject.SubjectContext;
import org.apache.shiro.web.mgt.DefaultWebSubjectFactory;
public class JwtDefaultSubjectFactory extends DefaultWebSubjectFactory {
@Override
public Subject createSubject(SubjectContext context) {
// Do not create session
context.setSessionCreationEnabled(false);
return super.createSubject(context); }}Copy the code
1. Create JwtUtil
This is usually a fixed notation with a lot of comments
package cn.coderymy.util;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.commons.codec.binary.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/* * In general, there are three methods in the utility class * to get the JwtToken, to get the information encapsulated in the JwtToken, to determine whether the JwtToken exists * 1.encode (), parameter = issuer, existence time, some other information =. The return value is the string * 2.decode () for JwtToken, and the argument is =JwtToken=. The return value is the load part of the key-value pair * 3.isverify (), the argument is =JwtToken=. The return value is whether the JwtToken exists * */
public class JwtUtil {
// Create a default secret key and algorithm for the constructor with no arguments
private static final String defaultbase64EncodedSecretKey = "badbabe";
private static final SignatureAlgorithm defaultsignatureAlgorithm = SignatureAlgorithm.HS256;
public JwtUtil(a) {
this(defaultbase64EncodedSecretKey, defaultsignatureAlgorithm);
}
private final String base64EncodedSecretKey;
private final SignatureAlgorithm signatureAlgorithm;
public JwtUtil(String secretKey, SignatureAlgorithm signatureAlgorithm) {
this.base64EncodedSecretKey = Base64.encodeBase64String(secretKey.getBytes());
this.signatureAlgorithm = signatureAlgorithm;
}
/* * This is where the JWT string is generated * the JWT string has three parts * 1. header * - the type of the current string, usually "JWT" * - which encryption algorithm, "HS256" or some other encryption algorithm * so it is usually fixed, Payload * iAT: the time when the JWT was generated * jti: the unique identifier of the JWT * iss: the person who signed the JWT, which is usually username or userId * exp: Expiration time * * */
public String encode(String iss, long ttlMillis, Map<String, Object> claims) {
// ISS signer, ttlMillis time of life, claims refers to some non-private information that is still wanted to be stored in JWT
if (claims == null) {
claims = new HashMap<>();
}
long nowMillis = System.currentTimeMillis();
JwtBuilder builder = Jwts.builder()
.setClaims(claims)
.setId(UUID.randomUUID().toString())//2. This is the unique identifier of the JWT. It is usually set to unique
.setIssuedAt(new Date(nowMillis))//1. This is the iAT generated by converting the current system time in milliseconds
.setSubject(iss)//3. The name of the user to whom the JWT is issued
.signWith(signatureAlgorithm, base64EncodedSecretKey);// This is where the algorithm and key used to generate JWT are generated
if (ttlMillis >= 0) {
long expMillis = nowMillis + ttlMillis;
Date exp = new Date(expMillis);//4. Expiration time, which is also generated in milliseconds, is generated using the current time + the duration passed in earlier
builder.setExpiration(exp);
}
return builder.compact();
}
// Pass in jwtToken to generate the corresponding fields such as username and password. Claim is a map
// Get all the key pairs in the load section
public Claims decode(String jwtToken) {
/ / get DefaultJwtParser
return Jwts.parser()
// Set the key for the signature
.setSigningKey(base64EncodedSecretKey)
// Set the JWT to parse
.parseClaimsJws(jwtToken)
.getBody();
}
// Check whether the jwtToken is valid
public boolean isVerify(String jwtToken) {
// This is the official validation rule, here only write a "validation algorithm", you can add your own
Algorithm algorithm = null;
switch (signatureAlgorithm) {
case HS256:
algorithm = Algorithm.HMAC256(Base64.decodeBase64(base64EncodedSecretKey));
break;
default:
throw new RuntimeException("This algorithm is not supported");
}
JWTVerifier verifier = JWT.require(algorithm).build();
verifier.verify(jwtToken); // If the check fails, an exception is thrown
1. The head and load parts have not been tampered with. 2. It's not expired
return true;
}
public static void main(String[] args) {
JwtUtil util = new JwtUtil("tom", SignatureAlgorithm.HS256);
// Use Tom as the secret key and HS256 encryption
Map<String, Object> map = new HashMap<>();
map.put("username"."tom");
map.put("password"."123456");
map.put("age".20);
String jwtToken = util.encode("tom".30000, map);
System.out.println(jwtToken);
util.decode(jwtToken).entrySet().forEach((entry) -> {
System.out.println(entry.getKey() + ":"+ entry.getValue()); }); }}Copy the code
2. Create JwtFilter
This means adding one more filter to Shiro’s interceptor, which you will need to register in your configuration file later
package cn.coderymy.filter;
import cn.coderymy.shiro.JwtToken;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.web.filter.AccessControlFilter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/* * Customize a Filter that blocks all requests to check whether the Token is carried * isAccessAllowed() Check whether the Token is valid * onAccessDenied() is used to log in without JwtToken. Access allowed after login succeeds, access denied after login fails * */
@Slf4j
public class JwtFilter extends AccessControlFilter {
/* * 1. Return true, shiro allows access to url * 2. If false is returned, Shiro will decide whether to allow access to the URL * */ based on the return value of onAccessDenied
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
log.warn("The isAccessAllowed method is called");
// Let it always return false to use the onAccessDenied() method
return false;
}
/** * Returns true indicating that the login passed */
@Override
protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
log.warn("OnAccessDenied method is called");
// This place is consistent with the front-end convention that requires the front-end to place the jwtToken in the Header part of the request
// So in the future, we need to put an Authorization in the Header, and the value is the corresponding Token
HttpServletRequest request = (HttpServletRequest) servletRequest;
String jwt = request.getHeader("Authorization");
log.info("Request Header contains jwtToken {}", jwt);
JwtToken jwtToken = new JwtToken(jwt);
/* *
try {
// Delegate realm to authenticate the login
// So this is where JwtRealm authentication is finally called
getSubject(servletRequest, servletResponse).login(jwtToken);
// That is, subject.login(token)
} catch (Exception e) {
e.printStackTrace();
onLoginFail(servletResponse);
// Call the following method to return an error message to the client
return false;
}
return true;
// If no exception is thrown in the execution method, the login is successful
}
// If the login fails, 401 status code is returned by default
private void onLoginFail(ServletResponse response) throws IOException {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
httpResponse.getWriter().write("login error"); }}Copy the code
3. Create JwtToken
It encapsulates the JWT string that needs to be passed
package cn.coderymy.shiro;
import org.apache.shiro.authc.AuthenticationToken;
// This is similar to UsernamePasswordToken
public class JwtToken implements AuthenticationToken {
private String jwt;
public JwtToken(String jwt) {
this.jwt = jwt;
}
@Override// Similar to the user name
public Object getPrincipal(a) {
return jwt;
}
@Override// Similar to password
public Object getCredentials(a) {
return jwt;
}
// Return JWT
}
Copy the code
4. JwtRealm
Creates a Realm for an authentication mode that determines whether JWT is valid
package cn.coderymy.realm;
import cn.coderymy.shiro.JwtToken;
import cn.coderymy.util.JwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
@Slf4j
public class JwtRealm extends AuthorizingRealm {
This Realm is dedicated to validating jwtTokens * not responsible for validating other tokens (UsernamePasswordToken) * */
@Override
public boolean supports(AuthenticationToken token) {
// This token is the jwtToken passed in from the filter
return token instanceof JwtToken;
}
/ / authorization
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
/ / certification
// This token is the jwtToken passed in from the filter
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String jwt = (String) token.getPrincipal();
if (jwt == null) {
throw new NullPointerException("JwtToken is not allowed to be empty");
}
/ / determine
JwtUtil jwtUtil = new JwtUtil();
if(! jwtUtil.isVerify(jwt)) {throw new UnknownAccountException();
}
// Verify that the user is real
String username = (String) jwtUtil.decode(jwt).get("username");// Check whether username exists in the database
log.info("Login using token"+username);
return new SimpleAuthenticationInfo(jwt,jwt,"JwtRealm");
// This returns something like an account password, but the jwtToken is a JWT string. You also need a class name for the Realm}}Copy the code
5. ShiroConfig
Configure some information
- Since sessions are not applicable, the custom Subject method is called by default in case of an error that would result from calling the getSession() method
- Some modifications, close SHiroDao, etc
- Registered JwtFilter
package cn.coderymy.config;
import cn.coderymy.filter.JwtFilter;
import cn.coderymy.realm.JwtRealm;
import cn.coderymy.shiro.JwtDefaultSubjectFactory;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SubjectFactory;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.AnonymousFilter;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
//springBoot integration JWT implementation has three different aspects, corresponding to the following ABC
@Configuration
public class ShiroConfig {
/* * a. Tell Shiro not to use the default DefaultSubject to create objects, because Session * */ cannot be created
@Bean
public SubjectFactory subjectFactory(a) {
return new JwtDefaultSubjectFactory();
}
@Bean
public Realm realm(a) {
return new JwtRealm();
}
@Bean
public DefaultWebSecurityManager securityManager(a) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(realm());
/* * b * */
// Disable the ShiroDAO function
DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
// There is no need to store anything from Shiro Session anywhere (including Http Session)
defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
securityManager.setSubjectDAO(subjectDAO);
// Disallow the Subject's getSession method
securityManager.setSubjectFactory(subjectFactory());
return securityManager;
}
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(a) {
ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
shiroFilter.setSecurityManager(securityManager());
shiroFilter.setLoginUrl("/unauthenticated");
shiroFilter.setUnauthorizedUrl("/unauthorized");
/* * c. Add a JWT Filter and register the jwtFilter to Shiro's Filter. * Specify that all requests except login and logout go through jwtFilter * */
Map<String, Filter> filterMap = new HashMap<>();
// The other two filters can be left unset
filterMap.put("anon".new AnonymousFilter());
filterMap.put("jwt".new JwtFilter());
filterMap.put("logout".new LogoutFilter());
shiroFilter.setFilters(filterMap);
/ / the interceptor
Map<String, String> filterRuleMap = new LinkedHashMap<>();
filterRuleMap.put("/login"."anon");
filterRuleMap.put("/logout"."logout");
filterRuleMap.put("/ * *"."jwt");
shiroFilter.setFilterChainDefinitionMap(filterRuleMap);
returnshiroFilter; }}Copy the code
Test 6.
package cn.coderymy.controller;
import cn.coderymy.util.JwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@Controller
public class LoginController {
@RequestMapping("/login")
public ResponseEntity<Map<String, String>> login(String username, String password) {
log.info("username:{},password:{}",username,password);
Map<String, String> map = new HashMap<>();
if (!"tom".equals(username) || !"123".equals(password)) {
map.put("msg"."Incorrect user name or password");
return ResponseEntity.ok(map);
}
JwtUtil jwtUtil = new JwtUtil();
Map<String, Object> chaim = new HashMap<>();
chaim.put("username", username);
String jwtToken = jwtUtil.encode(username, 5 * 60 * 1000, chaim);
map.put("msg"."Login successful");
map.put("token", jwtToken);
return ResponseEntity.ok(map);
}
@RequestMapping("/testdemo")
public ResponseEntity<String> testdemo(a) {
return ResponseEntity.ok("I love egg-fried rice."); }}Copy the code
4.3 Authorization Information
In the authorization section of JwtRealm, you can use JwtUtil.decode(JWT).get(“username”) to obtain username, use username to find the corresponding permission in the database, and then assign the permission to the user