Spring Security介紹
大部分系統(tǒng)荆萤,都需要認證和鑒權的功能。SpringBoot常用的安全框架spring security和shiro箕般。
shiro相對來說簡單易用,spring security功能更完善一點谦疾。
本文介紹spring security的集成方法纷闺,以及使用數(shù)據(jù)庫維護權限數(shù)據(jù),包括用戶凳谦、權限
使用數(shù)據(jù)庫維護用戶數(shù)據(jù)
數(shù)據(jù)庫表設計
這里簡化一下忆畅,直接實現(xiàn)User和Role的映射,而省略了Role和資源的映射尸执。
CREATE TABLE `Users` (
`UserId` int(11) NOT NULL AUTO_INCREMENT,
`UserName` varchar(45) NOT NULL,
`PassWord` varchar(100) NOT NULL,
`LockedFlag` tinyint(4) NOT NULL,
PRIMARY KEY (`UserId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `UserRole` (
`UserRoleId` int(11) NOT NULL AUTO_INCREMENT,
`UserId` int(11) NOT NULL,
`RoleId` int(11) NOT NULL,
PRIMARY KEY (`UserRoleId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `Role` (
`RoleId` int(11) NOT NULL AUTO_INCREMENT,
`RoleCode` varchar(45) NOT NULL,
`RoleDesc` varchar(200) DEFAULT NULL,
PRIMARY KEY (`RoleId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Mybatis代碼
如果你看過前幾章mybatis方面的內容家凯,可以直接自己實現(xiàn)一下,跳過Mybatis這部分內容如失。
bean層
//RoleBean
package com.it_laowu.springbootstudy.springbootstudydemo.bean;
import java.io.Serializable;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
@SuppressWarnings("serial")
public class RoleBean implements Serializable {
private int roleId;
private String roleCode;
private String roleDesc;
}
//UserBean
......省略package和import
@Data
@Accessors(chain = true)
@SuppressWarnings("serial")
public class UserBean implements Serializable {
private int userId;
private String userName;
private String passWord;
private String lockedFlag;
}
//UserRoleBean
......省略package和import
@Data
@Accessors(chain = true)
@SuppressWarnings("serial")
public class UserRoleBean implements Serializable {
private int userRoleId;
private int userId;
private int roleId;
}
Condition層
//RoleCondition
package com.it_laowu.springbootstudy.springbootstudydemo.bean.condition;
import com.it_laowu.springbootstudy.springbootstudydemo.bean.RoleBean;
import com.it_laowu.springbootstudy.springbootstudydemo.core.base.BaseCondition;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
public class RoleCondition extends BaseCondition {
private int roleId;
private String roleCode;
private String roleDesc;
@Override
public Class<?> getChildClass() {
return RoleBean.class;
}
}
//UserCondition
......省略package和import
@Data
@Accessors(chain = true)
public class UserCondition extends BaseCondition {
private int userId;
private String userName;
private String passWord;
private String lockedFlag;
@Override
public Class<?> getChildClass() {
return UserBean.class;
}
}
//UserRoleCondition
......省略package和import
@Data
@Accessors(chain = true)
public class UserRoleCondition extends BaseCondition {
private int userRoleId;
private int userId;
private int roleId;
@Override
public Class<?> getChildClass() {
return UserRoleBean.class;
}
}
dao層
//RoleDao
package com.it_laowu.springbootstudy.springbootstudydemo.dao;
import java.util.List;
import com.it_laowu.springbootstudy.springbootstudydemo.bean.RoleBean;
import com.it_laowu.springbootstudy.springbootstudydemo.bean.condition.RoleCondition;
import com.it_laowu.springbootstudy.springbootstudydemo.core.base.IBaseDao;
import org.apache.ibatis.annotations.Param;
public interface RoleDao extends IBaseDao<RoleBean,RoleCondition> {
List<RoleBean> getUserRolesByUserId(@Param("keyId") int userId);
}
//UserDao
......省略package和import
public interface UserDao extends IBaseDao<UserBean,UserCondition> {
UserBean findByName(@Param("username") String username);
}
//UserRoleDao
......省略package和import
public interface UserRoleDao extends IBaseDao<UserRoleBean,UserRoleCondition> {
}
service層
//IRoleService
package com.it_laowu.springbootstudy.springbootstudydemo.service;
import com.it_laowu.springbootstudy.springbootstudydemo.bean.RoleBean;
import com.it_laowu.springbootstudy.springbootstudydemo.bean.condition.RoleCondition;
import com.it_laowu.springbootstudy.springbootstudydemo.core.base.IBaseService;
public interface IRoleService extends IBaseService<RoleBean,RoleCondition> {
List<RoleBean> getUserRolesByUserId(@Param("keyId") int userId);
}
//IUserRoleService
......省略package和import
public interface IUserRoleService extends IBaseService<UserRoleBean,UserRoleCondition> {
}
//IUserService
......省略package和import
public interface IUserService extends IBaseService<UserBean,UserCondition> {
UserBean findByName(@Param("username") String username);
}
ServiceImpl層
//RoleServiceImpl
package com.it_laowu.springbootstudy.springbootstudydemo.service.impl;
import java.util.List;
import com.it_laowu.springbootstudy.springbootstudydemo.bean.RoleBean;
import com.it_laowu.springbootstudy.springbootstudydemo.bean.condition.RoleCondition;
import com.it_laowu.springbootstudy.springbootstudydemo.core.base.IBaseDao;
import com.it_laowu.springbootstudy.springbootstudydemo.dao.RoleDao;
import com.it_laowu.springbootstudy.springbootstudydemo.service.IRoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class RoleServiceImpl implements IRoleService {
@Autowired
public RoleDao roleDao;
@Override
public IBaseDao<RoleBean, RoleCondition> getBaseDao() {
return roleDao;
}
@Override
public List<RoleBean> getUserRolesByUserId(int userId) {
return roleDao.getUserRolesByUserId(userId);
}
}
//UserRoleServiceImpl
......省略package和import
@Service
public class UserRoleServiceImpl implements IUserRoleService {
@Override
public IBaseDao<UserRoleBean, UserRoleCondition> getBaseDao() {
return userRoleDao;
}
}
//UserServiceImpl
......省略package和import
@Service
public class UserServiceImpl implements IUserService {
@Autowired
public UserDao userDao;
@Override
public IBaseDao<UserBean, UserCondition> getBaseDao() {
return userDao;
}
@Override
public UserBean findByName(String username) {
return userDao.findByName(username);
}
}
mybatis.xml
role.mybatis.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.it_laowu.springbootstudy.springbootstudydemo.dao.RoleDao">
<resultMap id="RoleResultMap" type="com.it_laowu.springbootstudy.springbootstudydemo.bean.RoleBean">
<result column="roleId" property="roleId"/>
<result column="roleCode" property="roleCode"/>
<result column="roleDesc" property="roleDesc"/>
</resultMap>
<select id="findAll" resultMap="RoleResultMap">
select roleId,roleCode,`roleDesc`
from `Role`
<where>
<if test="conditionQC.roleId != 0">
and roleId = #{conditionQC.roleId}
</if>
<if test="conditionQC.roleCode != null and '' != conditionQC.roleCode">
and roleCode like concat('%',#{conditionQC.roleCode},'%')
</if>
<if test="conditionQC.roleDesc != null and '' != conditionQC.roleDesc">
and roleDesc like concat('%',#{conditionQC.roleDesc},'%')
</if>
</where>
<choose>
<when test="conditionQC.sortSql == null">
Order by roleId
</when>
<otherwise>
${conditionQC.sortSql}
</otherwise>
</choose>
</select>
<select id="findOne" resultMap="RoleResultMap">
select roleId,roleCode,`roleDesc`
from `Role`
where roleId = #{keyId}
</select>
<select id="getUserRolesByUserId" resultMap="RoleResultMap">
select r.roleId,r.roleCode,r.`roleDesc`
from `Role` r
left join `UserRole` ur ON r.RoleId = ur.RoleId
where ur.UserId = #{keyId}
</select>
<insert id="insert" parameterType="com.it_laowu.springbootstudy.springbootstudydemo.bean.RoleBean">
insert into `Role`(roleId,`roleCode`,`roleDesc`)
values(#{roleId},#{roleCode},#{roleDesc})
</insert>
<update id="update" parameterType="com.it_laowu.springbootstudy.springbootstudydemo.bean.RoleBean">
update `Role`
<set>
<if test="roleCode!=null"> `roleCode`=#{roleCode}, </if>
<if test="roleDesc!=null"> `roleDesc`=#{roleDesc}, </if>
</set>
where roleId = #{roleId}
</update>
<delete id="delete" parameterType="int">
delete from `Role` where roleId = #{keyId}
</delete>
</mapper>
user.mybatis.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.it_laowu.springbootstudy.springbootstudydemo.dao.UserDao">
<resultMap id="UserResultMap" type="com.it_laowu.springbootstudy.springbootstudydemo.bean.UserBean">
<result column="userId" property="userId"/>
<result column="userName" property="userName"/>
<result column="passWord" property="passWord"/>
<result column="lockedFlag" property="lockedFlag"/>
</resultMap>
<select id="findAll" resultMap="UserResultMap">
select userId,userName,`passWord`,lockedFlag
from `Users`
<where>
<if test="conditionQC.userId != 0">
and userId = #{conditionQC.userId}
</if>
<if test="conditionQC.userName != null and '' != conditionQC.userName">
and userName like concat('%',#{conditionQC.userName},'%')
</if>
<if test="conditionQC.passWord != null and '' != conditionQC.passWord">
and passWord like concat('%',#{conditionQC.passWord},'%')
</if>
<if test="conditionQC.lockedFlag != -1">
and lockedFlag = #{conditionQC.lockedFlag}
</if>
</where>
<choose>
<when test="conditionQC.sortSql == null">
Order by userId
</when>
<otherwise>
${conditionQC.sortSql}
</otherwise>
</choose>
</select>
<select id="findOne" resultMap="UserResultMap">
select userId,userName,`passWord`,lockedFlag
from `Users`
where userId = #{keyId}
</select>
<select id="findByName" resultMap="UserResultMap">
select userId,userName,`passWord`,lockedFlag
from `Users`
where userName = #{username}
</select>
<insert id="insert" parameterType="com.it_laowu.springbootstudy.springbootstudydemo.bean.UserBean">
insert into `Users`(userId,`userName`,`passWord`,lockedFlag)
values(#{userId},#{userName},#{passWord},#{lockedFlag})
</insert>
<update id="update" parameterType="com.it_laowu.springbootstudy.springbootstudydemo.bean.UserBean">
update `Users`
<set>
<if test="userName!=null"> `userName`=#{userName}, </if>
<if test="passWord!=null"> `passWord`=#{passWord}, </if>
<if test="lockedFlag!=null"> `lockedFlag`=#{lockedFlag}, </if>
</set>
where userId = #{userId}
</update>
<delete id="delete" parameterType="int">
delete from `Users` where userId = #{keyId}
</delete>
</mapper>
UserRole.mybatis.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.it_laowu.springbootstudy.springbootstudydemo.dao.UserRoleDao">
<resultMap id="UserRoleResultMap" type="com.it_laowu.springbootstudy.springbootstudydemo.bean.UserRoleBean">
<result column="userRoleId" property="userRoleId"/>
<result column="userId" property="userId"/>
<result column="roleId" property="roleId"/>
</resultMap>
<select id="findAll" resultMap="UserRoleResultMap">
select userRoleId,userId,`roleId`
from `UserRole`
<where>
<if test="conditionQC.userRoleId != 0">
and userRoleId = #{conditionQC.userRoleId}
</if>
<if test="conditionQC.userId != 0">
and userId = #{conditionQC.userId}
</if>
<if test="conditionQC.roleId != 0">
and roleId = #{conditionQC.roleId}
</if>
</where>
<choose>
<when test="conditionQC.sortSql == null">
Order by userRoleId
</when>
<otherwise>
${conditionQC.sortSql}
</otherwise>
</choose>
</select>
<select id="findOne" resultMap="UserRoleResultMap">
select userRoleId,userId,`RoleId`
from `UserRole`
where userRoleId = #{keyId}
</select>
<insert id="insert" parameterType="com.it_laowu.springbootstudy.springbootstudydemo.bean.UserRoleBean">
insert into `UserRole`(`userId`,`roleId`)
values(#{userId},#{roleId})
</insert>
<update id="update" parameterType="com.it_laowu.springbootstudy.springbootstudydemo.bean.UserRoleBean">
update `UserRole`
<set>
<if test="userId!=0"> `userId`=#{userId}, </if>
<if test="roleId!=0"> `roleId`=#{roleId}, </if>
</set>
where userRoleId = #{userRoleId}
</update>
<delete id="delete" parameterType="int">
delete from `UserRole` where userRoleId = #{keyId}
</delete>
</mapper>
Controller
因為Controller越來越多绊诲,而且都是實現(xiàn)Crud的功能,重復使用的頻率很高褪贵,所以做了個BaseController
package com.it_laowu.springbootstudy.springbootstudydemo.core.base;
import java.util.List;
import com.it_laowu.springbootstudy.springbootstudydemo.core.customexceptions.ConflictException;
import com.it_laowu.springbootstudy.springbootstudydemo.core.pagehelper.PageInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
public abstract class BaseController<Bean,Condition,IService extends IBaseService<Bean,Condition>> {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired(required = false)
public IService baseService;
@RequestMapping(value = "/list", method = RequestMethod.GET)
public List<Bean> orders(Condition condition,PageInfo pageinfo) {
return baseService.findAll(condition,pageinfo);
}
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public Bean order(@PathVariable(value = "id") int id) {
return baseService.findOne(id);
}
@RequestMapping(value = "/add", method = RequestMethod.POST)
public int add(@Validated @RequestBody Bean bean) {
return baseService.insert(bean);
}
@RequestMapping(value = "/{id}", method = RequestMethod.PUT)
public int update(@PathVariable(value = "id") int id, @RequestBody Bean bean) {
setBeanKeyId(bean, id);
return baseService.update(bean);
}
@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
public int delete(@PathVariable(value = "id") int id) {
return baseService.delete(id);
}
//其實就是update時掂之,設置一下主鍵
protected abstract void setBeanKeyId(Bean bean,int keyId) ;
}
有了這個,其他Controller就簡單了脆丁,比如:
@RestController
@RequestMapping(value = "/user")
public class UserController extends BaseController<UserBean,UserCondition,IUserService>{
@Override
protected void setBeanKeyId(UserBean bean, int keyId) {
bean.setUserId(keyId);
}
}
維護用戶數(shù)據(jù)時世舰,記得加密密碼。
這里提供一個密碼 admin
偎快,加密后:$2a$10$xQI1JKsTrkemGxEK1mpsAezGQeTDpSNVjfMCLrhgf8ImpCE2hKI1S
maven依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
springboot具有自動裝配機制冯乘,只要引入依賴,即使沒配置晒夹,也會啟動spring security
裆馒,配置一套默認的權限。
現(xiàn)在啟動項目丐怯,Console會自動生成用戶User
的密碼喷好,訪問API會彈出登錄提示界面,登錄后可以自由訪問頁面读跷。
Security代碼
以上都是數(shù)據(jù)庫維護用戶權限數(shù)據(jù)的代碼梗搅。
下面的代碼是集成Spring Security會用到的一些主要的代碼:
-
WebSecurityConfigurerAdapter
:實現(xiàn)Security的各種配置 -
AuthenticationProvider
:通過重寫authenticate
方法,實現(xiàn)用戶的校驗效览。 -
UserDetailsService
:通過重寫loadUserByUsername
方法无切,可以實現(xiàn)在數(shù)據(jù)庫中配置用戶權限。 -
MyAuthenticationFailureHandler
丐枉、MyAuthenticationSuccessHandler
:登錄成功/失敗反饋哆键。 -
resources/static/auth/login.html
:自制比較友好的登錄頁面。 -
resources/static/auth/logout.html
:自制比較友好的登出提示頁面瘦锹。 -
AdminController
:設置了登錄權限的一個頁面籍嘹,提供完整的訪問測試闪盔。
配置文件WebSecurityConfigurerAdapter
這個類提供了Security的各種開關、特性的配置辱士。
首先泪掀,新建一個WebSecurityConfigurerAdapter
的實現(xiàn)類:
package com.it_laowu.springbootstudy.springbootstudydemo.core.config;
import com.it_laowu.springbootstudy.springbootstudydemo.core.auth.MyAuthenticationFailureHandler;
import com.it_laowu.springbootstudy.springbootstudydemo.core.auth.MyAuthenticationProvider;
import com.it_laowu.springbootstudy.springbootstudydemo.core.auth.MyAuthenticationSuccessHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyAuthenticationProvider myAuthenticationProvider;
@Autowired
private MyAuthenticationFailureHandler myAuthenticationFailureHandler;
@Autowired
private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(myAuthenticationProvider);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.httpBasic()
.and()
.authorizeRequests()
.antMatchers("/user/**").permitAll()
.antMatchers("/role/**").permitAll()
.antMatchers("/userrole/**").permitAll()
.antMatchers("/order/**").permitAll()
.antMatchers("/auth/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
//默認是/login,不用改
.loginPage("/login")
.successHandler(myAuthenticationSuccessHandler)
.failureHandler(myAuthenticationFailureHandler)
.permitAll()
.and()
.logout()
//默認為/logout颂碘,不用改
.logoutUrl("/logout")
.logoutSuccessUrl("/auth/logout.html") //默認跳轉到 /login
.deleteCookies("JSESSIONID"); //默認也是會刪除cookie的
}
}
}
針對這部分代碼做一些簡單的說明:
-
authorizeRequests
:規(guī)定了哪些頁面需要特定權限异赫,哪些需要用戶登錄,哪些不需要凭涂。 -
formLogin
:定義了API登錄頁面祝辣,以及成功失敗的反饋處理(Handler) -
logout
:和login一樣,大部分使用默認參數(shù)即可切油。 -
passwordEncoder
:注入一個加密器蝙斜,之前提供的密碼,也是用這個加密器加密的澎胡。
認證相關代碼
MyAuthenticationProvider
:用戶認證
package com.it_laowu.springbootstudy.springbootstudydemo.core.auth;
......省略import
@Component
public class MyAuthenticationProvider implements AuthenticationProvider {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Resource
private MyUserDetailsService myUserDetailsService;
@Resource
private PasswordEncoder passwordEncoder;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
//根據(jù)輸入的用戶密碼孕荠,讀取數(shù)據(jù)庫中信息
String username = authentication.getName();
String password = (String) authentication.getCredentials();
MyUserDetails user = (MyUserDetails) myUserDetailsService.loadUserByUsername(username);
//判斷是否有效用戶
if (!user.isEnabled()) {
throw new DisabledException("USER DISABLE");
} else if (!user.isAccountNonLocked()) {
throw new LockedException("USER LOCKED");
} else if (!user.isAccountNonExpired()) {
throw new AccountExpiredException("USER EXPIRED");
} else if (!user.isCredentialsNonExpired()) {
throw new CredentialsExpiredException("USER LOGOUT");
}
//驗證密碼
if (!passwordEncoder.matches(password, user.getPassword())) {
throw new BadCredentialsException("PASSWORD INVALID!");
}
logger.info(String.format("用戶%s登錄成功", username));
return new UsernamePasswordAuthenticationToken(user, password, user.getAuthorities());
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
MyUserDetailsService
:用戶數(shù)據(jù)處理,這里是和數(shù)據(jù)庫交互的關鍵點
package com.it_laowu.springbootstudy.springbootstudydemo.core.auth;
......省略import
@Component
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private IUserService userService;
@Autowired
private IRoleService roleService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserBean bean = userService.findByName(username);
if (bean == null){
throw new UsernameNotFoundException("User "+ username +" didn't exist.");
}
//獲得角色信息
List<RoleBean> roles = roleService.getUserRolesByUserId(bean.getUserId());
//格式轉化
List<GrantedAuthority> authority = roles.stream().map(i->new SimpleGrantedAuthority(i.getRoleCode())).collect(Collectors.toList());
return new MyUserDetails(bean.getUserName(),bean.getPassWord(),bean.getLockedFlag()==1,authority);
}
}
MyUserDetails
:UserDetails實現(xiàn)類攻谁,保存數(shù)據(jù)
package com.it_laowu.springbootstudy.springbootstudydemo.core.auth;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
@SuppressWarnings("serial")
public class MyUserDetails implements UserDetails {
protected Integer id;
private String username;
private String password;
private boolean lockedFlag;
public MyUserDetails(String username, String password, boolean lockedFlag,Collection<? extends GrantedAuthority> roles) {
this.username = username;
this.password = password;
this.lockedFlag = lockedFlag;
this.authorities = roles;
}
public Collection<? extends GrantedAuthority> authorities;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return !lockedFlag;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
MyAuthenticationSuccessHandler
:認證成功反饋
package com.it_laowu.springbootstudy.springbootstudydemo.core.auth;
......省略import
@Component
public class MyAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler{
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
//登錄成功返回
ResultBody resultBody = new ResultBody("200", "登錄成功");
//設置返回請求頭
response.setContentType("application/json;charset=utf-8");
//寫出流
PrintWriter out = response.getWriter();
ObjectMapper mapper = new ObjectMapper();
out.write(mapper.writeValueAsString(resultBody));
out.flush();
out.close();
}
}
MyAuthenticationFailureHandler
:認證失敗反饋
package com.it_laowu.springbootstudy.springbootstudydemo.core.auth;
......省略import
@Component
public class MyAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
//登錄失敗信息返回
ResultBody resultBody = new ResultBody("401", "登錄失敗:"+exception.getLocalizedMessage());
//設置返回請求頭
response.setContentType("application/json;charset=utf-8");
//寫出流
PrintWriter out = response.getWriter();
ObjectMapper mapper = new ObjectMapper();
out.write(mapper.writeValueAsString(resultBody));
out.flush();
out.close();
}
}
雜項代碼
/resources/static/auth/login.html
就是提供一個友好的登錄頁面稚伍,方便測試。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Login</title>
</head>
<body>
<h2>Login</h2>
<form id="loginForm" action="/login" method="post">
用戶:<input type="text" id="username" name="username"><br /><br />
密碼:<input type="password" id="password" name="password"><br /><br />
<button id="btnlogin" type="button">Login</button>
</form>
<span id="msg" name="msg"></h2>
<script src="http://libs.baidu.com/jquery/2.1.4/jquery.min.js"></script>
<script type="text/javascript">
$("#btnlogin").click(function () {
$.ajax({
type: "POST",
url: "/springboot-study-demo/login",
data: $("#loginForm").serialize(),
dataType: "JSON",
success: function (data) {
console.log(data);
$("#msg").text(data.message);
}
});
});
</script>
</body>
</html>
/resources/static/auth/logout.html
登出成功的反饋頁面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Logout</title>
</head>
<body>
<h2>Log out success !!</h2>
</body>
</html>
AdminController
業(yè)務Controller戚宦,所有url均需登錄用戶个曙。
@RestController
@RequestMapping(value = "/admin")
public class AdminController {
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public String info(@PathVariable(value = "id") int id) {
return "i am an administrator";
}
}
測試一下
- 訪問
/admin/1
,直接跳轉到/login
受楼,訪問失敗 - 訪問
/auth/login.html
垦搬,先測密碼錯誤,再測成功艳汽。 - 訪問
/admin/1
猴贰,返回i am an administrator
。 - 訪問
/logout
河狐,跳轉到/auth/logout.html
. - 訪問
/admin/1
米绕,直接跳轉到/login
,再次訪問失敗
這里要注意馋艺,部分瀏覽器(比如Chrome)會自動記錄用戶密碼栅干,導致logout后,再次訪問/admin/1
還是成功的捐祠。使用火狐就沒這個問題碱鳞。