sso单点登录

文章目录

目录

文章目录

前言

一、sso结构实现

二、使用步骤

2.1 建一个spring cloud 项目

 2.2 common下的core的配置

 2.3 实现系统的业务微服务

2.4 sso模块的编写  

总结



前言

单点登录(SingleSignOn,SSO),就是通过用户的一次性鉴别登录。当用户在身份认证服务器上登录一次以后,即可获得访问单点登录系统中其他关联系统和应用软件的权限,同时这种实现是不需要管理员对用户的登录状态或其他信息进行修改的,这意味着在多个应用系统中,用户只需一次登录就可以访问所有相互信任的应用系统。这种方式减少了由登录产生的时间消耗,辅助了用户管理,是比较流行的


一、sso结构实现

sso单点登录_第1张图片

 

二、使用步骤

2.1 建一个spring cloud 项目

sso单点登录_第2张图片

 

 在总的父工程下添加 父工程使用

2.3.12.RELEASE 的springboot项目创建 然后添加以下
    pom
    
    
        1.8
        UTF-8
        UTF- 8
        Hoxton.SR8
        2.2.3.RELEASE
    
    
    
        
            
                org.springframework.cloud
                spring-cloud-dependencies
                ${spring-cloud.version}
                pom
                import
            
            
                com.alibaba.cloud
                spring-cloud-alibaba-dependencies
                ${spring-cloud-alibaba.version}
                pom
                import
            
        
    

其他二级模块和三级模块都是使用maven项目 每一个maven模块最后添加一个spring boot的启动类 

 2.2 common下的core的配置

引入依赖


    
        org.projectlombok
        lombok
    
    
        com.auth0
        java-jwt
        3.18.3
    
    
        com.alibaba
        fastjson
        1.2.83
    
    
        com.baomidou
        mybatis-plus-boot-starter
        3.5.2
    

添加实体类 工具类 

三个实体类 

@Data
@TableName(value = "cc_admin")
public class Admin implements Serializable {
    /**
     * 管理员表id
     */
    @TableId(value = "id",type = IdType.AUTO)
    private Integer id;

    /**
     * 管理员id
     */
    private String adminId;

    /**
     * 管理员用户名
     */
    private String adminUsername;

    /**
     * 管理员密码
     */
    private String adminPassword;

    /**
     * 管理员真实姓名
     */
    private String adminRealname;

    /**
     * 管理员密钥
     */
    private String salt;

    /**
     * 角色id
     */
    private String roleId;

    /**
     * 部门id
     */
    private String deptId;

    /**
     * 管理员性别
     */
    private String adminSex;

    /**
     * 管理员联系方式
     */
    private String adminTelephone;

    /**
     * 管理员年龄
     */
    private Integer adminAge;

    /**
     * 逻辑删除0(false)未删除,1(true)已删除
     */
    private Boolean isDeleted;

    /**
     * 创建时间
     */
    private Date gmtCreate;

    /**
     * 修改时间
     */
    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();
    }
}

/**
 * 权限
 * @TableName cc_permission
 */
@Data
@TableName(value = "cc_permission")
public class Permission implements Serializable {
    /**
     * 编号
     */
    @TableId(value = "id")
    private String id;

    /**
     * 所属上级
     */
    private String pid;

    /**
     * 名称
     */
    private String name;

    /**
     * 类型(1:菜单,2:按钮)
     */
    private Integer type;

    /**
     * 权限值
     */
    private String permissionValue;

    /**
     * 访问路径
     */
    private String path;

    /**
     * 组件路径
     */
    private String component;

    /**
     * 图标
     */
    private String icon;

    /**
     * 状态(0:正常,1:禁用)
     */
    private Integer status;

    /**
     * 逻辑删除 1(true)已删除, 0(false)未删除
     */
    private Integer isDeleted;

    /**
     * 创建时间
     */
    private Date gmtCreate;

    /**
     * 更新时间
     */
    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 {
    /**
     * 角色表id
     */
    @TableId(value = "id",type = IdType.AUTO)
    private Integer id;

    /**
     * 角色id
     */
    private String roleId;

    /**
     * 角色名称
     */
    private String roleName;

    /**
     * 角色描述
     */
    private String roleDescription;

    /**
     * 部门id
     */
    private String deptId;

    /**
     * 角色状态0(启用)1(禁用)
     */
    private Integer roleStatus;

    /**
     * 逻辑删除0(false)未删除,1(true)已删除
     */
    private Boolean isDeleted;

    /**
     * 创建时间
     */
    private Date gmtCreate;

    /**
     * 修改时间
     */
    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工具类和前端数据处理vo

public class JwtUtil {
    private static String sign="xxx";
    //1.生成jwt的token方法
    public static String createJWT(Map map){
        //定义头信息
        Map head= new HashMap<>();
        head.put("alg","HS256");
        head.put("typ","JWT");
        //定义颁发时间
        Date iat=new Date();
        //过期时间
        Calendar expire = Calendar.getInstance();
        expire.set(Calendar.SECOND,24*3600);//24*3600
        Date expireTime = expire.getTime();

        String token = JWT.create()
                //头信息
                .withHeader(head)
                //设置颁发时间
                .withIssuedAt(iat)
                //设置过期时间
                .withExpiresAt(expireTime)
                //自定义的内容
                .withClaim("userinfo",map)
                //签名
                .sign(Algorithm.HMAC256(sign));
        return token;
    }

    //2.校验token是否有效
    public static boolean verifyToken(String token){
        //获取一个JWT校验对象
        JWTVerifier build = JWT.require(Algorithm.HMAC256(sign)).build();
        try {
            //调用校验功能
            DecodedJWT verify = build.verify(token);
            return true;
        }catch (Exception e){
            System.out.println("token无效");
            return false;
        }

    }

    //3.从token中获取相关的载荷内容
    public static Map getTokenChaim(String token){
        //获取一个JWT校验对象
        JWTVerifier build = JWT.require(Algorithm.HMAC256(sign)).build();
        //调用校验功能
        DecodedJWT verify = build.verify(token);
        Claim loginInfo = verify.getClaim("userinfo");
        return loginInfo.asMap();
    }
}

vo 返回给前端的统一数据处理类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result {
    private Integer code;
    private String msg;
    private T data;

    public Result(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }
}

 2.3 实现系统的业务微服务

在modules添加需要的依赖


        
            com.xin
            xin-common-core
            0.0.1-SNAPSHOT
        
        
            mysql
            mysql-connector-java
            5.1.49
        
<-- web启动类 -->
        
            org.springframework.boot
            spring-boot-starter-web
        
<-- nacos注册中心 -->
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-nacos-discovery
        
<-- 微服务之间的调用 -->
        
            org.springframework.cloud
            spring-cloud-starter-openfeign
        
    

在modules下新建system子模块 dao层和service使用mybatisX插件生成 启动类使用spring boot的

sso单点登录_第3张图片

主要展示服务之间的控制层 

@RestController
@RequestMapping("system/permission")
public class PermissionController {
    @Autowired
    private PermissionService permissionService;

    @GetMapping("getPermissionByUserid/{userid}")
    public List 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 wrapper=new QueryWrapper<>();
        wrapper.eq("admin_username",name);
        Admin admin = adminService.getOne(wrapper);
        return admin;
    }
}

配置文件

 

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 sso模块的编写  

添加依赖


    
    
        com.xin
        xin-common-core
        0.0.1-SNAPSHOT
    

<-- rdis启动 -->
    
        org.springframework.boot
        spring-boot-starter-data-redis
    

    
        org.springframework.boot
        spring-boot-starter-web
    
    
        com.alibaba.cloud
        spring-cloud-starter-alibaba-nacos-discovery
    
    
        org.springframework.boot
        spring-boot-starter-security
    
    
        org.springframework.cloud
        spring-cloud-starter-openfeign
    
    
        com.alibaba.cloud
        spring-cloud-starter-alibaba-sentinel
    

 sso单点登录_第4张图片

@Configuration
public class MySecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserService userService;

    @Autowired
    private StringRedisTemplate redisTemplate;
    @Bean
    public PasswordEncoder passwordEncoder() {
        //加盐加密: 原密码+盐===>哈希加密===>密文
        //   原密码===>哈希加密===>密文 [存储大量常见的密文]
        //123456+随机产生盐===>密文
        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();
            //获取账号和权限
            String username = authentication.getPrincipal().toString();
            Collection authorities = authentication.getAuthorities().stream().map(item->item.getAuthority()).collect(Collectors.toList());
            //根据账号和权限生产token;
            Map map=new HashMap<>();
            map.put("username",username);
            map.put("authorities",authorities);
            String token = JwtUtil.createJWT(map);

            //存入redis中。
            redisTemplate.opsForValue().set(token,"",30, TimeUnit.MINUTES);

            Result result=new Result<>(2000,"登录成功",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 result=new Result<>(5000,"登录失败");
            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"));
    }
}

两个微服务的调用 


@FeignClient(value = "cai-system")
public interface AdminFeign {
    //建议:如果是服务之间的调用直接返回对象,如果是前端调用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 getPermissionByUserid(@PathVariable Integer userid);
}

自定义登录规则


@Service
public class UserService implements UserDetailsService {
    @Autowired
    private AdminFeign adminFeign;
    @Autowired
    private PermissionFeign permissionFeign;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //1.根据用户名查询用户信息
        Admin admin = adminFeign.getByName(username);
        if(admin!=null){
            //2.根据当前用户查询该用户具有的权限。
            List permissions = permissionFeign.getPermissionByUserid(admin.getId());
            Collection 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;
    }
}

主启动类


/**
 * 排除多余的自动装配类可开启openfeign
 */
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@EnableFeignClients
public class SsoApp {
    public static void main(String[] args) {
        SpringApplication.run(SsoApp.class,args);
    }
}

配置文件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

#解决多个FeignClientSpecification' could not be registered
#开启重写多个feign接口
spring.main.allow-bean-definition-overriding=true

 这样SSo模块就完成了

 

 

 

 

 

 

 

 

 

 

 


总结

待补充

你可能感兴趣的:(微服务,spring,cloud,spring,boot)