Article directory
Table of contents
1. Realization of sso structure
2.1 Build a spring cloud project
2.2 core configuration under common
foreword
Single sign-on (SingleSignOn, SSO) is to log in through the user's one-time authentication. when the user is in authentication server After logging in once, you can gain access to other related systems and application software in the single sign-on system. At the same time, this implementation does not require the administrator to modify the user's login status or other information, which means that multiple In the application system, users only need to log in once to access all mutually trusted application systems. This method reduces the time consumption caused by login and assists user management, which is more popular
1. Realization of sso structure
2. Use steps
2.1 Build a spring cloud project
Add the parent project under the total parent project to use
2.3.12.RELEASE's springboot project creation and then add the following
<packaging>pom</packaging> <!--Define the version number--> <properties> <java.version>1.8</java.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF- 8</project.reporting.outputEncoding> <spring-cloud.version>Hoxton.SR8</spring-cloud.version> <spring-cloud-alibaba.version>2.2.3.RELEASE</spring-cloud-alibaba.version> </properties> <!--jar gotta manage it's only responsible jar The management is not responsible for the download--> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>${spring-cloud-alibaba.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
Other secondary modules and tertiary modules use the maven project. Each maven module finally adds a spring boot startup class.
2.2 core configuration under common
Introduce dependencies
<dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.18.3</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.83</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.2</version> </dependency> </dependencies>
Add Entity Class Tool Class
three entity classes
@Data @TableName(value = "cc_admin") public class Admin implements Serializable { /** * admin table id */ @TableId(value = "id",type = IdType.AUTO) private Integer id; /** * administrator id */ private String adminId; /** * Admin username */ private String adminUsername; /** * administrator password */ private String adminPassword; /** * Administrator's real name */ private String adminRealname; /** * admin key */ private String salt; /** * role id */ private String roleId; /** * department id */ private String deptId; /** * Admin gender */ private String adminSex; /** * Administrator contact information */ private String adminTelephone; /** * admin age */ private Integer adminAge; /** * Tombstone 0 (false) not deleted, 1 (true) deleted */ private Boolean isDeleted; /** * creation time */ private Date gmtCreate; /** * Change the time */ private Date gmtModified; private static final long serialVersionUID = 1L; @Override public boolean equals(Object that) { if (this == that) { return true; } if (that == null) { return false; } if (getClass() != that.getClass()) { return false; } Admin other = (Admin) that; return (this.getId() == null ? other.getId() == null : this.getId().equals(other.getId())) && (this.getAdminId() == null ? other.getAdminId() == null : this.getAdminId().equals(other.getAdminId())) && (this.getAdminUsername() == null ? other.getAdminUsername() == null : this.getAdminUsername().equals(other.getAdminUsername())) && (this.getAdminPassword() == null ? other.getAdminPassword() == null : this.getAdminPassword().equals(other.getAdminPassword())) && (this.getAdminRealname() == null ? other.getAdminRealname() == null : this.getAdminRealname().equals(other.getAdminRealname())) && (this.getSalt() == null ? other.getSalt() == null : this.getSalt().equals(other.getSalt())) && (this.getRoleId() == null ? other.getRoleId() == null : this.getRoleId().equals(other.getRoleId())) && (this.getDeptId() == null ? other.getDeptId() == null : this.getDeptId().equals(other.getDeptId())) && (this.getAdminSex() == null ? other.getAdminSex() == null : this.getAdminSex().equals(other.getAdminSex())) && (this.getAdminTelephone() == null ? other.getAdminTelephone() == null : this.getAdminTelephone().equals(other.getAdminTelephone())) && (this.getAdminAge() == null ? other.getAdminAge() == null : this.getAdminAge().equals(other.getAdminAge())) && (this.getIsDeleted() == null ? other.getIsDeleted() == null : this.getIsDeleted().equals(other.getIsDeleted())) && (this.getGmtCreate() == null ? other.getGmtCreate() == null : this.getGmtCreate().equals(other.getGmtCreate())) && (this.getGmtModified() == null ? other.getGmtModified() == null : this.getGmtModified().equals(other.getGmtModified())); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((getId() == null) ? 0 : getId().hashCode()); result = prime * result + ((getAdminId() == null) ? 0 : getAdminId().hashCode()); result = prime * result + ((getAdminUsername() == null) ? 0 : getAdminUsername().hashCode()); result = prime * result + ((getAdminPassword() == null) ? 0 : getAdminPassword().hashCode()); result = prime * result + ((getAdminRealname() == null) ? 0 : getAdminRealname().hashCode()); result = prime * result + ((getSalt() == null) ? 0 : getSalt().hashCode()); result = prime * result + ((getRoleId() == null) ? 0 : getRoleId().hashCode()); result = prime * result + ((getDeptId() == null) ? 0 : getDeptId().hashCode()); result = prime * result + ((getAdminSex() == null) ? 0 : getAdminSex().hashCode()); result = prime * result + ((getAdminTelephone() == null) ? 0 : getAdminTelephone().hashCode()); result = prime * result + ((getAdminAge() == null) ? 0 : getAdminAge().hashCode()); result = prime * result + ((getIsDeleted() == null) ? 0 : getIsDeleted().hashCode()); result = prime * result + ((getGmtCreate() == null) ? 0 : getGmtCreate().hashCode()); result = prime * result + ((getGmtModified() == null) ? 0 : getGmtModified().hashCode()); return result; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", adminId=").append(adminId); sb.append(", adminUsername=").append(adminUsername); sb.append(", adminPassword=").append(adminPassword); sb.append(", adminRealname=").append(adminRealname); sb.append(", salt=").append(salt); sb.append(", roleId=").append(roleId); sb.append(", deptId=").append(deptId); sb.append(", adminSex=").append(adminSex); sb.append(", adminTelephone=").append(adminTelephone); sb.append(", adminAge=").append(adminAge); sb.append(", isDeleted=").append(isDeleted); sb.append(", gmtCreate=").append(gmtCreate); sb.append(", gmtModified=").append(gmtModified); sb.append(", serialVersionUID=").append(serialVersionUID); sb.append("]"); return sb.toString(); } }
/** * permissions * @TableName cc_permission */ @Data @TableName(value = "cc_permission") public class Permission implements Serializable { /** * serial number */ @TableId(value = "id") private String id; /** * Affiliated superior */ private String pid; /** * name */ private String name; /** * type(1:menu,2:button) */ private Integer type; /** * permission value */ private String permissionValue; /** * access path */ private String path; /** * component path */ private String component; /** * icon */ private String icon; /** * status (0: normal, 1: disabled) */ private Integer status; /** * Tombstone 1 (true) deleted, 0 (false) not deleted */ private Integer isDeleted; /** * creation time */ private Date gmtCreate; /** * update time */ private Date gmtModified; private static final long serialVersionUID = 1L; @Override public boolean equals(Object that) { if (this == that) { return true; } if (that == null) { return false; } if (getClass() != that.getClass()) { return false; } Permission other = (Permission) that; return (this.getId() == null ? other.getId() == null : this.getId().equals(other.getId())) && (this.getPid() == null ? other.getPid() == null : this.getPid().equals(other.getPid())) && (this.getName() == null ? other.getName() == null : this.getName().equals(other.getName())) && (this.getType() == null ? other.getType() == null : this.getType().equals(other.getType())) && (this.getPermissionValue() == null ? other.getPermissionValue() == null : this.getPermissionValue().equals(other.getPermissionValue())) && (this.getPath() == null ? other.getPath() == null : this.getPath().equals(other.getPath())) && (this.getComponent() == null ? other.getComponent() == null : this.getComponent().equals(other.getComponent())) && (this.getIcon() == null ? other.getIcon() == null : this.getIcon().equals(other.getIcon())) && (this.getStatus() == null ? other.getStatus() == null : this.getStatus().equals(other.getStatus())) && (this.getIsDeleted() == null ? other.getIsDeleted() == null : this.getIsDeleted().equals(other.getIsDeleted())) && (this.getGmtCreate() == null ? other.getGmtCreate() == null : this.getGmtCreate().equals(other.getGmtCreate())) && (this.getGmtModified() == null ? other.getGmtModified() == null : this.getGmtModified().equals(other.getGmtModified())); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((getId() == null) ? 0 : getId().hashCode()); result = prime * result + ((getPid() == null) ? 0 : getPid().hashCode()); result = prime * result + ((getName() == null) ? 0 : getName().hashCode()); result = prime * result + ((getType() == null) ? 0 : getType().hashCode()); result = prime * result + ((getPermissionValue() == null) ? 0 : getPermissionValue().hashCode()); result = prime * result + ((getPath() == null) ? 0 : getPath().hashCode()); result = prime * result + ((getComponent() == null) ? 0 : getComponent().hashCode()); result = prime * result + ((getIcon() == null) ? 0 : getIcon().hashCode()); result = prime * result + ((getStatus() == null) ? 0 : getStatus().hashCode()); result = prime * result + ((getIsDeleted() == null) ? 0 : getIsDeleted().hashCode()); result = prime * result + ((getGmtCreate() == null) ? 0 : getGmtCreate().hashCode()); result = prime * result + ((getGmtModified() == null) ? 0 : getGmtModified().hashCode()); return result; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", pid=").append(pid); sb.append(", name=").append(name); sb.append(", type=").append(type); sb.append(", permissionValue=").append(permissionValue); sb.append(", path=").append(path); sb.append(", component=").append(component); sb.append(", icon=").append(icon); sb.append(", status=").append(status); sb.append(", isDeleted=").append(isDeleted); sb.append(", gmtCreate=").append(gmtCreate); sb.append(", gmtModified=").append(gmtModified); sb.append(", serialVersionUID=").append(serialVersionUID); sb.append("]"); return sb.toString(); } }
@Data @TableName(value = "cc_role") public class Role implements Serializable { /** * role table id */ @TableId(value = "id",type = IdType.AUTO) private Integer id; /** * role id */ private String roleId; /** * Role Name */ private String roleName; /** * role description */ private String roleDescription; /** * department id */ private String deptId; /** * Character status 0 (enabled) 1 (disabled) */ private Integer roleStatus; /** * Tombstone 0 (false) not deleted, 1 (true) deleted */ private Boolean isDeleted; /** * creation time */ private Date gmtCreate; /** * Change the time */ private Date gmtModified; private static final long serialVersionUID = 1L; @Override public boolean equals(Object that) { if (this == that) { return true; } if (that == null) { return false; } if (getClass() != that.getClass()) { return false; } Role other = (Role) that; return (this.getId() == null ? other.getId() == null : this.getId().equals(other.getId())) && (this.getRoleId() == null ? other.getRoleId() == null : this.getRoleId().equals(other.getRoleId())) && (this.getRoleName() == null ? other.getRoleName() == null : this.getRoleName().equals(other.getRoleName())) && (this.getRoleDescription() == null ? other.getRoleDescription() == null : this.getRoleDescription().equals(other.getRoleDescription())) && (this.getDeptId() == null ? other.getDeptId() == null : this.getDeptId().equals(other.getDeptId())) && (this.getRoleStatus() == null ? other.getRoleStatus() == null : this.getRoleStatus().equals(other.getRoleStatus())) && (this.getIsDeleted() == null ? other.getIsDeleted() == null : this.getIsDeleted().equals(other.getIsDeleted())) && (this.getGmtCreate() == null ? other.getGmtCreate() == null : this.getGmtCreate().equals(other.getGmtCreate())) && (this.getGmtModified() == null ? other.getGmtModified() == null : this.getGmtModified().equals(other.getGmtModified())); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((getId() == null) ? 0 : getId().hashCode()); result = prime * result + ((getRoleId() == null) ? 0 : getRoleId().hashCode()); result = prime * result + ((getRoleName() == null) ? 0 : getRoleName().hashCode()); result = prime * result + ((getRoleDescription() == null) ? 0 : getRoleDescription().hashCode()); result = prime * result + ((getDeptId() == null) ? 0 : getDeptId().hashCode()); result = prime * result + ((getRoleStatus() == null) ? 0 : getRoleStatus().hashCode()); result = prime * result + ((getIsDeleted() == null) ? 0 : getIsDeleted().hashCode()); result = prime * result + ((getGmtCreate() == null) ? 0 : getGmtCreate().hashCode()); result = prime * result + ((getGmtModified() == null) ? 0 : getGmtModified().hashCode()); return result; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); sb.append(" ["); sb.append("Hash = ").append(hashCode()); sb.append(", id=").append(id); sb.append(", roleId=").append(roleId); sb.append(", roleName=").append(roleName); sb.append(", roleDescription=").append(roleDescription); sb.append(", deptId=").append(deptId); sb.append(", roleStatus=").append(roleStatus); sb.append(", isDeleted=").append(isDeleted); sb.append(", gmtCreate=").append(gmtCreate); sb.append(", gmtModified=").append(gmtModified); sb.append(", serialVersionUID=").append(serialVersionUID); sb.append("]"); return sb.toString(); } }
jwt tool class and front-end data processing vo
public class JwtUtil { private static String sign="xxx"; //1. Generate the token method of jwt public static String createJWT(Map<String,Object> map){ //Define header information Map<String,Object> head= new HashMap<>(); head.put("alg","HS256"); head.put("typ","JWT"); //Define issue time Date iat=new Date(); //Expiration Calendar expire = Calendar.getInstance(); expire.set(Calendar.SECOND,24*3600);//24*3600 Date expireTime = expire.getTime(); String token = JWT.create() //header information .withHeader(head) //Set issue time .withIssuedAt(iat) //set expiration time .withExpiresAt(expireTime) //custom content .withClaim("userinfo",map) //sign .sign(Algorithm.HMAC256(sign)); return token; } //2. Check whether the token is valid public static boolean verifyToken(String token){ //Get a JWT verification object JWTVerifier build = JWT.require(Algorithm.HMAC256(sign)).build(); try { //Call the validation function DecodedJWT verify = build.verify(token); return true; }catch (Exception e){ System.out.println("token invalid"); return false; } } //3. Obtain the relevant payload content from the token public static Map<String,Object> getTokenChaim(String token){ //Get a JWT verification object JWTVerifier build = JWT.require(Algorithm.HMAC256(sign)).build(); //Call the validation function DecodedJWT verify = build.verify(token); Claim loginInfo = verify.getClaim("userinfo"); return loginInfo.asMap(); } }
vo returns to the unified data processing class of the front end
@Data @NoArgsConstructor @AllArgsConstructor public class Result<T> { private Integer code; private String msg; private T data; public Result(Integer code, String msg) { this.code = code; this.msg = msg; } }
2.3 Realize the business microservices of the system
Add required dependencies to modules
<dependencies> <dependency> <groupId>com.xin</groupId> <artifactId>xin-common-core</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.49</version> </dependency> <-- web startup class --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <-- nacos registration center --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <-- calls between microservices --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> </dependencies>
Create a new system sub-module under modules. The dao layer and service are generated using the mybatisX plug-in. The startup class uses spring boot
Mainly display the control layer between services
@RestController @RequestMapping("system/permission") public class PermissionController { @Autowired private PermissionService permissionService; @GetMapping("getPermissionByUserid/{userid}") public List<Permission> getPermissionByUserid(@PathVariable Integer userid){ return permissionService.selectPermissionByUserid(userid); } }
@RestController @RequestMapping("system/admin") public class AdminController { @Autowired private AdminService adminService; @GetMapping("/getByName/{name}") public Admin getByName(@PathVariable String name){ QueryWrapper<Admin> wrapper=new QueryWrapper<>(); wrapper.eq("admin_username",name); Admin admin = adminService.getOne(wrapper); return admin; } }
configuration file
server.port=8087 spring.application.name=cai-system spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/caicai?serverTimezone=Asia/Shanghai spring.datasource.password=root spring.datasource.username=root mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl spring.cloud.nacos.discovery.server-addr=localhost:8848
2.4 Writing of sso module
add dependencies
<dependencies> <!--Introduce common-core--> <dependency> <groupId>com.xin</groupId> <artifactId>xin-common-core</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <-- rdis start --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> </dependencies>
@Configuration public class MySecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserService userService; @Autowired private StringRedisTemplate redisTemplate; @Bean public PasswordEncoder passwordEncoder() { //Salt encryption: original password + salt ===> hash encryption ===> ciphertext // Original password ===> hash encryption ===> ciphertext [store a large number of common ciphertexts] //123456+ randomly generated salt ===> ciphertext return new BCryptPasswordEncoder(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userService).passwordEncoder(passwordEncoder()); } @Override protected void configure(HttpSecurity http) throws Exception { http.formLogin() .successHandler(successHandler()) .failureHandler(failureHandler()) .loginProcessingUrl("/login").permitAll(); http.cors(); http.csrf().disable(); http.authorizeRequests().anyRequest().authenticated(); } private AuthenticationSuccessHandler successHandler() { return (request, response, authentication) -> { response.setContentType("application/json;charset=utf-8"); PrintWriter writer = response.getWriter(); //Get account and permissions String username = authentication.getPrincipal().toString(); Collection<String> authorities = authentication.getAuthorities().stream().map(item->item.getAuthority()).collect(Collectors.toList()); //Produce token s based on account numbers and permissions; Map<String,Object> map=new HashMap<>(); map.put("username",username); map.put("authorities",authorities); String token = JwtUtil.createJWT(map); //Stored in redis. redisTemplate.opsForValue().set(token,"",30, TimeUnit.MINUTES); Result<String> result=new Result<>(2000,"login successful",token); String jsonString = JSON.toJSONString(result); writer.print(jsonString); writer.flush(); writer.close(); }; } private AuthenticationFailureHandler failureHandler() { return (request, response, e) -> { response.setContentType("application/json;charset=utf-8"); PrintWriter writer = response.getWriter(); Result<String> result=new Result<>(5000,"Login failed"); String jsonString = JSON.toJSONString(result); writer.print(jsonString); writer.flush(); writer.close(); }; } public static void main(String[] args) { BCryptPasswordEncoder passwordEncoder=new BCryptPasswordEncoder(); System.out.println(passwordEncoder.encode("123456")); } }
Two microservice calls
@FeignClient(value = "cai-system") public interface AdminFeign { //Suggestion: If the call between services directly returns the object, if the front end calls CommonResult @GetMapping("/system/admin/getByName/{name}") public Admin getByName(@PathVariable String name); }
@FeignClient(value = "cai-system") public interface PermissionFeign { @GetMapping("/system/permission/getPermissionByUserid/{userid}") public List<Permission> getPermissionByUserid(@PathVariable Integer userid); }
custom login rules
@Service public class UserService implements UserDetailsService { @Autowired private AdminFeign adminFeign; @Autowired private PermissionFeign permissionFeign; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { //1. Query user information based on user name Admin admin = adminFeign.getByName(username); if(admin!=null){ //2. Query the permissions of the user according to the current user. List<Permission> permissions = permissionFeign.getPermissionByUserid(admin.getId()); Collection<SimpleGrantedAuthority> authorities = permissions.stream().filter(item->item.getPermissionValue()!=null).filter(item->item.getType()==2).map(item->new SimpleGrantedAuthority(item.getPermissionValue())).collect(Collectors.toList()); User user = new User(admin.getAdminUsername(),admin.getAdminPassword(),authorities); return user; } return null; } }
main startup class
/** * Exclude redundant automatic assembly classes to open openfeign */ @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) @EnableFeignClients public class SsoApp { public static void main(String[] args) { SpringApplication.run(SsoApp.class,args); } }
Configuration file application.properties
server.port=8088 spring.application.name=cai-sso spring.cloud.nacos.discovery.server-addr=localhost:8848 spring.redis.host=localhost spring.redis.port=6379 #Resolve multiple FeignClientSpecification' could not be registered #Enable rewriting multiple feign interfaces spring.main.allow-bean-definition-overriding=true
In this way, the SSo module is completed
Summarize
To be added