SpringBoot中用SpringSecurity实现用户登录并返回其拥有哪些角色
注:在使用springsecurity之前我们用普通的登录方式
1、前端发来登录请求会带上username,password
2、后端根据username去数据库查询用户,查不到表示用户不存在,查到了再拿password去和数据库进行比对
3、如果比对一样把它存入session,后续实行任何操作都先从session中先判断user存不存在
其实一直以来我对用户登录的理解就是这么简单,然后我发现有很多地方的登录都五花八门,方式多了,自己也就会变得
有点糊涂,所以我在这里就使用博客形式来理清一下思路,如果用springsecurity的话这些步骤其实框架都给我们做了,这也是这篇博客的意义所在...
下面就使用springsecurity来进行登录,有人会问为什么不用shiro来实现,其实springsecurity这个技术也存在很多年了,并不是一个
新技术了,如果使用ssm框架确实配合shiro会香一点,但是在springboot中使用springsecurity会更方便,因为很多事情boot都帮我们做了
下面就来和我一起coding吧....
一、创建hr(user)表、role(角色)表、hr_role(用户角色关联表)
1 create table `hr` ( 2 `id` int(11) not null auto_increment comment 'hrid', 3 `name` varchar(32) default null comment '姓名', 4 `phone` char(11) default null comment '手机号码', 5 `telephone` varchar(16) default null comment '住宅电话', 6 `address` varchar(64) default null comment '联系地址', 7 `enabled` tinyint(1) default '1', 8 `username` varchar(255) default null comment '用户名', 9 `password` varchar(255) default null comment '密码', 10 `userface` varchar(255) default null, 11 `remark` varchar(255) default null, 12 primary key (`id`) 13 ) engine=innodb auto_increment=13 default charset=utf8
1 create table `role` ( 2 `id` int(11) not null auto_increment, 3 `name` varchar(64) default null, 4 `namezh` varchar(64) default null comment '角色名称', 5 primary key (`id`) 6 ) engine=innodb auto_increment=22 default charset=utf8
1 create table `hr_role` ( 2 `id` int(11) not null auto_increment, 3 `hrid` int(11) default null, 4 `rid` int(11) default null, 5 primary key (`id`), 6 key `rid` (`rid`), 7 key `hr_role_ibfk_1` (`hrid`), 8 constraint `hr_role_ibfk_1` foreign key (`hrid`) references `hr` (`id`) on delete cascade, 9 constraint `hr_role_ibfk_2` foreign key (`rid`) references `role` (`id`) 10 ) engine=innodb auto_increment=76 default charset=utf8
二、先不急做其他的,可以先测试一下,下面我随便写了个controller【创建springboot工程时记得加上springsecurity依赖】
1 package org.yybb.securitylogin.controller; 2 3 import org.springframework.web.bind.annotation.getmapping; 4 import org.springframework.web.bind.annotation.restcontroller; 5 6 /** 7 * @author: ben 8 * @createtime: 2020-03-12 06:00 9 */ 10 @restcontroller 11 public class hellocontroller { 12 @getmapping("/hello") 13 public string hello() { 14 return "hello "; 15 } 16 }
三、访问hello
他会自动跳转到springsecurity的登录页,然后我们就行登录,默认用户名是user密码会在控制台随机打印出来的,然后登录过后就能访问了
只要引入security依赖就会把所以接口保护起来,下面我们连接上数据库使用
四、创建实体类
注意:使用springsecurity用户类必须实现userdetail
1 package org.yybb.securitylogin.model; 2 3 import org.springframework.security.core.grantedauthority; 4 import org.springframework.security.core.userdetails.userdetails; 5 6 import java.util.collection; 7 import java.util.list; 8 9 /** 10 * @author: ben 11 * @createtime: 2020-03-07 12:08 12 */ 13 public class hr implements userdetails { 14 private integer id; 15 16 private string name; 17 18 private string phone; 19 20 private string telephone; 21 22 private string address; 23 24 private boolean enabled; 25 26 private string username; 27 28 private string password; 29 30 private string userface; 31 32 private string remark; 33 34 private list<role>roles; 35 36 public list<role> getroles() { 37 return roles; 38 } 39 40 public void setroles(list<role> roles) { 41 this.roles = roles; 42 } 43 44 public integer getid() { 45 return id; 46 } 47 48 public void setid(integer id) { 49 this.id = id; 50 } 51 52 public string getname() { 53 return name; 54 } 55 56 public void setname(string name) { 57 this.name = name; 58 } 59 60 public string getphone() { 61 return phone; 62 } 63 64 public void setphone(string phone) { 65 this.phone = phone; 66 } 67 68 public string gettelephone() { 69 return telephone; 70 } 71 72 public void settelephone(string telephone) { 73 this.telephone = telephone; 74 } 75 76 public string getaddress() { 77 return address; 78 } 79 80 public void setaddress(string address) { 81 this.address = address; 82 } 83 84 /* 底下返回,不删除就会有两个 85 public boolean getenabled() { 86 return enabled; 87 }*/ 88 89 public void setenabled(boolean enabled) { 90 this.enabled = enabled; 91 } 92 93 public string getusername() { 94 return username; 95 } 96 97 /** 98 * 账号是否过期、未过期 99 * 100 * @return true 101 */ 102 @override 103 public boolean isaccountnonexpired() { 104 return true; 105 } 106 107 /** 108 * 账号是否被锁定、未锁定 109 * 110 * @return true 111 */ 112 @override 113 public boolean isaccountnonlocked() { 114 return true; 115 } 116 117 /** 118 * 密码是否过期、未过期 119 * 120 * @return true 121 */ 122 @override 123 public boolean iscredentialsnonexpired() { 124 return true; 125 } 126 127 @override 128 public boolean isenabled() { 129 return enabled; 130 } 131 132 public void setusername(string username) { 133 this.username = username; 134 } 135 136 /** 137 * 返回用户角色 138 * @return 139 */ 140 @override 141 public collection<? extends grantedauthority> getauthorities() { 142 return null; 143 } 144 145 public string getpassword() { 146 return password; 147 } 148 149 public void setpassword(string password) { 150 this.password = password; 151 } 152 153 public string getuserface() { 154 return userface; 155 } 156 157 public void setuserface(string userface) { 158 this.userface = userface; 159 } 160 161 public string getremark() { 162 return remark; 163 } 164 165 public void setremark(string remark) { 166 this.remark = remark; 167 } 168 }
1 package org.yybb.securitylogin.model; 2 3 import java.io.serializable; 4 5 /** 6 * @author: ben 7 * @createtime: 2020-03-10 13:43 8 */ 9 public class role implements serializable { 10 private integer id; 11 12 private string name; 13 14 private string namezh; 15 16 public integer getid() { 17 return id; 18 } 19 20 public void setid(integer id) { 21 this.id = id; 22 } 23 24 public string getname() { 25 return name; 26 } 27 28 public void setname(string name) { 29 this.name = name == null ? null : name.trim(); 30 } 31 32 public string getnamezh() { 33 return namezh; 34 } 35 36 public void setnamezh(string namezh) { 37 this.namezh = namezh; 38 } 39 }
1 package org.yybb.securitylogin.model; 2 3 /** 4 * @author: ben 5 * @createtime: 2020-03-07 13:40 6 */ 7 public class respbean { 8 private integer status; 9 private string msg; 10 private object obj; 11 12 public static respbean success(string msg) { 13 return new respbean(200, msg, null); 14 } 15 16 public static respbean success(string msg, object obj) { 17 return new respbean(200, msg, obj); 18 } 19 20 public static respbean error(string msg) { 21 return new respbean(500, msg, null); 22 } 23 24 public static respbean error(string msg, object obj) { 25 return new respbean(500, msg, obj); 26 } 27 28 private respbean() { 29 } 30 31 private respbean(integer status, string msg, object obj) { 32 this.status = status; 33 this.msg = msg; 34 this.obj = obj; 35 } 36 37 public integer getstatus() { 38 return status; 39 } 40 41 public void setstatus(integer status) { 42 this.status = status; 43 } 44 45 public string getmsg() { 46 return msg; 47 } 48 49 public void setmsg(string msg) { 50 this.msg = msg; 51 } 52 53 public object getobj() { 54 return obj; 55 } 56 57 public void setobj(object obj) { 58 this.obj = obj; 59 } 60 }
五、创建service和mapper和mapper.xml[service必须实现userdetaillservice接口]
1 package org.yybb.securitylogin.service; 2 3 import org.springframework.beans.factory.annotation.autowired; 4 import org.springframework.security.core.userdetails.userdetails; 5 import org.springframework.security.core.userdetails.userdetailsservice; 6 import org.springframework.security.core.userdetails.usernamenotfoundexception; 7 import org.springframework.stereotype.service; 8 import org.yybb.securitylogin.mapper.hrmapper; 9 import org.yybb.securitylogin.model.hr; 10 11 /** 12 * @author: ben 13 * @createtime: 2020-03-12 06:17 14 */ 15 @service 16 public class hrservice implements userdetailsservice { 17 @autowired 18 hrmapper hrmapper; 19 @override 20 public userdetails loaduserbyusername(string username) throws usernamenotfoundexception { 21 hr hr = hrmapper.loaduserbyusername(username); 22 if (hr==null){ 23 throw new usernamenotfoundexception("用户不存在"); 24 } 25 return hr; 26 } 27 }
1 package org.yybb.securitylogin.mapper; 2 3 import org.apache.ibatis.annotations.mapper; 4 import org.yybb.securitylogin.model.hr; 5 6 /** 7 * @author: ben 8 * @createtime: 2020-03-12 06:19 9 */ 10 @mapper 11 public interface hrmapper { 12 13 hr loaduserbyusername(string username); 14 }
1 <?xml version="1.0" encoding="utf-8" ?> 2 <!doctype mapper public "-//mybatis.org//dtd mapper 3.0//en" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > 3 <mapper namespace="org.yybb.securitylogin.mapper.hrmapper"> 4 <resultmap id="baseresultmap" type="org.yybb.securitylogin.model.hr"> 5 <id column="id" property="id" jdbctype="integer"/> 6 <result column="name" property="name" jdbctype="varchar"/> 7 <result column="phone" property="phone" jdbctype="char"/> 8 <result column="telephone" property="telephone" jdbctype="varchar"/> 9 <result column="address" property="address" jdbctype="varchar"/> 10 <result column="enabled" property="enabled" jdbctype="bit"/> 11 <result column="username" property="username" jdbctype="varchar"/> 12 <result column="password" property="password" jdbctype="varchar"/> 13 <result column="userface" property="userface" jdbctype="varchar"/> 14 <result column="remark" property="remark" jdbctype="varchar"/> 15 </resultmap> 16 <select id="loaduserbyusername" resultmap="baseresultmap"> 17 select * from hr where username=#{username} 18 </select> 19 </mapper>
六、创建配置类
1 package org.yybb.securitylogin.config; 2 3 import org.springframework.beans.factory.annotation.autowired; 4 import org.springframework.context.annotation.bean; 5 import org.springframework.context.annotation.configuration; 6 import org.springframework.security.config.annotation.authentication.builders.authenticationmanagerbuilder; 7 import org.springframework.security.config.annotation.web.configuration.websecurityconfigureradapter; 8 import org.springframework.security.crypto.bcrypt.bcryptpasswordencoder; 9 import org.springframework.security.crypto.password.passwordencoder; 10 import org.yybb.securitylogin.service.hrservice; 11 12 /** 13 * @author: ben 14 * @createtime: 2020-03-12 06:28 15 */ 16 @configuration 17 public class securityconfig extends websecurityconfigureradapter { 18 19 @autowired 20 hrservice hrservice; 21 @bean 22 passwordencoder passwordencoder() { 23 return new bcryptpasswordencoder(); 24 } 25 26 @override 27 protected void configure(authenticationmanagerbuilder auth) throws exception { 28 auth.userdetailsservice(hrservice); 29 } 30 31 }
再测试就可以用数据库里面的用户进行登录啦.....
下面开始配置登录成功、失败、注销
1 package org.yybb.securitylogin.config; 2 3 import com.fasterxml.jackson.databind.objectmapper; 4 import org.springframework.beans.factory.annotation.autowired; 5 import org.springframework.context.annotation.bean; 6 import org.springframework.context.annotation.configuration; 7 import org.springframework.security.authentication.*; 8 import org.springframework.security.config.annotation.authentication.builders.authenticationmanagerbuilder; 9 import org.springframework.security.config.annotation.web.builders.httpsecurity; 10 import org.springframework.security.config.annotation.web.configuration.websecurityconfigureradapter; 11 import org.springframework.security.core.authentication; 12 import org.springframework.security.core.authenticationexception; 13 import org.springframework.security.crypto.bcrypt.bcryptpasswordencoder; 14 import org.springframework.security.crypto.password.passwordencoder; 15 import org.springframework.security.web.authentication.authenticationfailurehandler; 16 import org.springframework.security.web.authentication.authenticationsuccesshandler; 17 import org.springframework.security.web.authentication.logout.logoutsuccesshandler; 18 import org.yybb.securitylogin.model.hr; 19 import org.yybb.securitylogin.model.respbean; 20 import org.yybb.securitylogin.service.hrservice; 21 22 import javax.servlet.servletexception; 23 import javax.servlet.http.httpservletrequest; 24 import javax.servlet.http.httpservletresponse; 25 import java.io.ioexception; 26 import java.io.printwriter; 27 28 /** 29 * @author: ben 30 * @createtime: 2020-03-12 06:28 31 */ 32 @configuration 33 public class securityconfig extends websecurityconfigureradapter { 34 35 @autowired 36 hrservice hrservice; 37 38 @bean 39 passwordencoder passwordencoder() { 40 return new bcryptpasswordencoder(); 41 } 42 43 @override 44 protected void configure(authenticationmanagerbuilder auth) throws exception { 45 auth.userdetailsservice(hrservice); 46 } 47 48 @override 49 protected void configure(httpsecurity http) throws exception { 50 http.authorizerequests() 51 .anyrequest().authenticated()//所有请求必须认证过才能访问[没有配置myfilter,decisionmanager之前] 52 .and() 53 .formlogin() 54 .usernameparameter("username") 55 .passwordparameter("password") 56 //真正的登录接口,必须是key-value形式 57 .loginprocessingurl("/login.do") 58 //返回未登录json 59 .loginpage("/login") 60 //登录成功回调 61 .successhandler(new authenticationsuccesshandler() { 62 @override //authentication登录成功的用户信息保存地 63 public void onauthenticationsuccess(httpservletrequest request, httpservletresponse response, authentication authentication) throws ioexception, servletexception { 64 response.setcontenttype("application/json;charset=utf-8"); 65 printwriter out = response.getwriter(); 66 //从authentication得到对象强转成hr对象 67 hr hr = (hr) authentication.getprincipal(); 68 //避免返回给页面 69 hr.setpassword(null); 70 respbean success = respbean.success("登录成功!", hr); 71 //转成字符串以便输出 72 string str = new objectmapper().writevalueasstring(success); 73 out.write(str); 74 out.flush(); 75 out.close(); 76 } 77 }).failurehandler(new authenticationfailurehandler() { 78 @override //登录失败 79 public void onauthenticationfailure(httpservletrequest request, httpservletresponse response, authenticationexception exception) throws ioexception, servletexception { 80 response.setcontenttype("application/json;charset=utf-8"); 81 printwriter out = response.getwriter(); 82 respbean error = respbean.error("登录失败!"); 83 if (exception instanceof lockedexception) { 84 error.setmsg("账户被锁定,请联系管理员!"); 85 } else if (exception instanceof credentialsexpiredexception) { 86 error.setmsg("密码过期,请联系管理员!"); 87 } else if (exception instanceof accountexpiredexception) { 88 error.setmsg("账户过期,请联系管理员!"); 89 } else if (exception instanceof disabledexception) { 90 error.setmsg("账户被禁用,请联系管理员!"); 91 } else if (exception instanceof badcredentialsexception) { 92 error.setmsg("用户名或密码输入错误,请重新输入!"); 93 } 94 //转成字符串以便输出 95 out.write(new objectmapper().writevalueasstring(error)); 96 out.flush(); 97 out.close(); 98 } 99 }).permitall() 100 .and() 101 .logout() 102 .logoutsuccesshandler(new logoutsuccesshandler() { 103 @override //注销功能,默认接口logout 104 public void onlogoutsuccess(httpservletrequest request, httpservletresponse response, authentication authentication) throws ioexception, servletexception { 105 response.setcontenttype("application/json;charset=utf-8"); 106 printwriter out = response.getwriter(); 107 out.write(new objectmapper().writevalueasstring(respbean.success("注销成功!"))); 108 out.flush(); 109 out.close(); 110 } 111 }).permitall() 112 //postman测试 113 .and() 114 .csrf().disable(); 115 } 116 }
测试
测试是成功了,但是角色没有,下面把返回角色查询出来就行,大家应该注意到,我的实体类别hr里面返回的角色是return null;
所以首先把它加在hr
1 /** 2 * 返回用户角色 3 * @return 4 */ 5 @override 6 public collection<? extends grantedauthority> getauthorities() { 7 list<simplegrantedauthority>authorities=new arraylist<>(roles.size()); 8 for (role role : roles) { 9 authorities.add(new simplegrantedauthority(role.getname())); 10 } 11 return authorities; 12 }
登录成功之后在service中给用户设置角色
1 //设置用户角色 2 hr.setroles(hrmapper.gethrrolesbyid(hr.getid()));
给mapper加上方法,给xml加上sql
1 /** 2 * 根据用户id查询他拥有的角色 3 * @param id 4 * @return 5 */ 6 list<role> gethrrolesbyid(integer id);
1 <select id="gethrrolesbyid" resulttype="org.yybb.securitylogin.model.role">
2 select r.* from role r ,hr_role hrr where hrr.rid=r.id and hrr.hrid=#{id}
3 </select>
最终测试
以上就是用springsecurity实现用户登录返回对应角色的全过程。。